diff --git a/Cargo.lock b/Cargo.lock index 715f5d5..9ce298d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,20 +14,20 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.26.2", + "gimli 0.27.3", ] [[package]] name = "addr2line" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.27.2", + "gimli 0.28.1", ] [[package]] @@ -36,25 +36,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", -] - [[package]] name = "aead" version = "0.5.2" @@ -67,32 +48,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aes" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher 0.4.4", @@ -101,71 +59,37 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead 0.4.3", - "aes 0.7.5", - "cipher 0.3.0", - "ctr 0.8.0", - "ghash 0.4.4", - "subtle", -] - -[[package]] -name = "aes-gcm" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead 0.5.2", - "aes 0.8.2", + "aead", + "aes", "cipher 0.4.4", - "ctr 0.9.2", - "ghash 0.5.0", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug 0.3.0", + "ctr", + "ghash", + "subtle 2.5.0", ] [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -173,21 +97,24 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "aho-corasick" -version = "1.0.1" +name = "allocator-api2" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" -dependencies = [ - "memchr", -] +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "always-assert" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4436e0292ab1bb631b42973c61205e704475fe8126af845c8d923c0996328127" [[package]] name = "android-tzdata" @@ -215,58 +142,58 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "approx" @@ -278,5123 +205,11076 @@ dependencies = [ ] [[package]] -name = "arc-swap" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" - -[[package]] -name = "array-bytes" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" - -[[package]] -name = "arrayref" -version = "0.3.7" +name = "aquamarine" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "074b80d14d0240b6ce94d68f059a2d26a5d77280ae142662365a21ef6e2594ef" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.65", +] [[package]] -name = "arrayvec" -version = "0.5.2" +name = "ark-bls12-377" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] [[package]] -name = "arrayvec" -version = "0.7.4" +name = "ark-bls12-381" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] [[package]] -name = "asn1-rs" -version = "0.3.1" +name = "ark-ec" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" dependencies = [ - "asn1-rs-derive 0.1.0", - "asn1-rs-impl", - "displaydoc", - "nom", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", "num-traits", - "rusticata-macros", - "thiserror", - "time", + "zeroize", ] [[package]] -name = "asn1-rs" -version = "0.5.2" +name = "ark-ff" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" dependencies = [ - "asn1-rs-derive 0.4.0", - "asn1-rs-impl", - "displaydoc", - "nom", + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", "num-traits", - "rusticata-macros", - "thiserror", - "time", + "paste", + "rustc_version 0.4.0", + "zeroize", ] [[package]] -name = "asn1-rs-derive" -version = "0.1.0" +name = "ark-ff-asm" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "proc-macro2", "quote", "syn 1.0.109", - "synstructure", ] [[package]] -name = "asn1-rs-derive" -version = "0.4.0" +name = "ark-ff-macros" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ + "num-bigint", + "num-traits", "proc-macro2", "quote", "syn 1.0.109", - "synstructure", ] [[package]] -name = "asn1-rs-impl" -version = "0.1.0" +name = "ark-poly" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", ] [[package]] -name = "asn1_der" -version = "0.7.6" +name = "ark-serialize" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] [[package]] -name = "async-channel" -version = "1.8.0" +name = "ark-serialize-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "async-io" -version = "1.13.0" +name = "ark-std" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ - "async-lock", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite", - "log", - "parking", - "polling", - "rustix 0.37.19", - "slab", - "socket2", - "waker-fn", + "num-traits", + "rand", ] [[package]] -name = "async-lock" -version = "2.7.0" +name = "array-bytes" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" -dependencies = [ - "event-listener", -] +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" [[package]] -name = "async-recursion" -version = "1.0.4" +name = "array-bytes" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] +checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] -name = "async-trait" -version = "0.1.68" +name = "arrayref" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] -name = "asynchronous-codec" -version = "0.6.1" +name = "arrayvec" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" dependencies = [ - "bytes", - "futures-sink", - "futures-util", - "memchr", - "pin-project-lite 0.2.9", + "nodrop", ] [[package]] -name = "atomic-waker" -version = "1.1.1" +name = "arrayvec" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] -name = "atty" -version = "0.2.14" +name = "asn1-rs" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "asn1-rs-derive" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] [[package]] -name = "backtrace" -version = "0.3.67" +name = "asn1-rs-impl" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "addr2line 0.19.0", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.6.2", - "object 0.30.3", - "rustc-demangle", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base16ct" -version = "0.1.1" +name = "assert_matches" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +name = "asset-test-utils" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "assets-common", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "pallet-assets", + "pallet-balances", + "pallet-collator-selection", + "pallet-session", + "pallet-xcm", + "pallet-xcm-bridge-hub-router", + "parachains-common", + "parachains-runtimes-test-utils", + "parity-scale-codec", + "polkadot-parachain-primitives", + "sp-consensus-aura", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", +] [[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +name = "assets-common" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "impl-trait-for-tuples", + "log", + "pallet-asset-conversion", + "pallet-asset-tx-payment", + "pallet-xcm", + "parachains-common", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", +] [[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +name = "async-backing-primitives" +version = "0.9.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "sp-api", + "sp-consensus-slots", +] [[package]] -name = "base64ct" -version = "1.6.0" +name = "async-channel" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] [[package]] -name = "basic-toml" -version = "0.1.2" +name = "async-channel" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ - "serde", + "concurrent-queue", + "event-listener-strategy 0.5.2", + "futures-core", + "pin-project-lite 0.2.14", ] [[package]] -name = "beef" -version = "0.5.2" +name = "async-executor" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "serde", + "async-task", + "concurrent-queue", + "fastrand 2.1.0", + "futures-lite 2.3.0", + "slab", ] [[package]] -name = "bincode" -version = "1.3.3" +name = "async-fs" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ - "serde", + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", ] [[package]] -name = "bindgen" -version = "0.64.0" +name = "async-io" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 1.0.109", + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", ] [[package]] -name = "bitflags" -version = "1.3.2" +name = "async-io" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.7.0", + "rustix 0.38.34", + "slab", + "tracing", + "windows-sys 0.52.0", +] [[package]] -name = "bitvec" -version = "1.0.1" +name = "async-lock" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "funty", - "radium", - "tap", - "wyz", + "event-listener 2.5.3", ] [[package]] -name = "blake2" -version = "0.10.6" +name = "async-lock" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "digest 0.10.7", + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "pin-project-lite 0.2.14", ] [[package]] -name = "blake2b_simd" -version = "1.0.1" +name = "async-net" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f" dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "constant_time_eq", + "async-io 1.13.0", + "blocking", + "futures-lite 1.13.0", ] [[package]] -name = "blake2s_simd" -version = "1.0.1" +name = "async-process" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "constant_time_eq", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.34", + "windows-sys 0.48.0", ] [[package]] -name = "blake3" -version = "1.3.3" +name = "async-signal" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "cc", + "async-io 2.3.2", + "async-lock 3.3.0", + "atomic-waker", "cfg-if", - "constant_time_eq", + "futures-core", + "futures-io", + "rustix 0.38.34", + "signal-hook-registry", + "slab", + "windows-sys 0.52.0", ] [[package]] -name = "block-buffer" -version = "0.7.3" +name = "async-task" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] -name = "block-buffer" -version = "0.9.0" +name = "async-trait" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "generic-array 0.14.7", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "asynchronous-codec" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" dependencies = [ - "generic-array 0.14.7", + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite 0.2.14", ] [[package]] -name = "block-modes" -version = "0.7.0" +name = "atoi" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" dependencies = [ - "block-padding 0.2.1", - "cipher 0.2.5", + "num-traits", ] [[package]] -name = "block-padding" -version = "0.1.5" +name = "atomic-take" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] +checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" [[package]] -name = "block-padding" -version = "0.2.1" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] -name = "bounded-collections" -version = "0.1.6" +name = "auto_impl" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3888522b497857eb606bf51695988dba7096941822c1bcf676e3a929a9ae7a0" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ - "log", - "parity-scale-codec", - "scale-info", - "serde", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "bs58" -version = "0.4.0" +name = "autocfg" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] -name = "bstr" -version = "1.4.0" +name = "backtrace" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ - "memchr", - "serde", + "addr2line 0.21.0", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.32.2", + "rustc-demangle", ] [[package]] -name = "build-helper" -version = "0.1.1" +name = "base-x" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" -dependencies = [ - "semver 0.6.0", -] +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] -name = "bumpalo" -version = "3.12.1" +name = "base16ct" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] -name = "byte-slice-cast" -version = "1.2.2" +name = "base64" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] -name = "byte-tools" -version = "0.3.1" +name = "base64" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "bytemuck" -version = "1.13.1" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "byteorder" -version = "1.4.3" +name = "beef" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] [[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +name = "binary-merkle-tree" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "hash-db", + "log", +] [[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" +name = "bincode" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "cc", - "libc", - "pkg-config", + "serde", ] [[package]] -name = "camino" -version = "1.1.4" +name = "bindgen" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "serde", + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease 0.2.20", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.65", ] [[package]] -name = "cargo-platform" -version = "0.1.2" +name = "bip39" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ + "bitcoin_hashes", + "rand", + "rand_core 0.6.4", "serde", + "unicode-normalization", ] [[package]] -name = "cargo_metadata" -version = "0.15.4" +name = "bitcoin_hashes" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.17", - "serde", - "serde_json", - "thiserror", -] +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" [[package]] -name = "cc" -version = "1.0.83" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "ccm" -version = "0.3.0" +name = "bitflags" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" -dependencies = [ - "aead 0.3.2", - "cipher 0.2.5", - "subtle", -] +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] -name = "cexpr" -version = "0.6.0" +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "nom", + "funty", + "radium", + "serde", + "tap", + "wyz", ] [[package]] -name = "cfg-expr" -version = "0.10.3" +name = "blake2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" dependencies = [ - "smallvec", + "byte-tools", + "crypto-mac 0.7.0", + "digest 0.8.1", + "opaque-debug 0.2.3", ] [[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" +name = "blake2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] [[package]] -name = "chacha20" -version = "0.9.1" +name = "blake2-rfc" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", + "arrayvec 0.4.12", + "constant_time_eq 0.1.5", ] [[package]] -name = "chacha20poly1305" -version = "0.10.1" +name = "blake2b_simd" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ - "aead 0.5.2", - "chacha20", - "cipher 0.4.4", - "poly1305", - "zeroize", + "arrayref", + "arrayvec 0.7.4", + "constant_time_eq 0.3.0", ] [[package]] -name = "chrono" -version = "0.4.31" +name = "blake2s_simd" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets 0.48.5", + "arrayref", + "arrayvec 0.7.4", + "constant_time_eq 0.3.0", ] [[package]] -name = "cid" -version = "0.8.6" +name = "blake3" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ - "core2", - "multibase", - "multihash 0.16.3", - "serde", - "unsigned-varint", + "arrayref", + "arrayvec 0.7.4", + "cc", + "cfg-if", + "constant_time_eq 0.3.0", ] [[package]] -name = "cipher" -version = "0.2.5" +name = "block-buffer" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ "generic-array 0.14.7", ] [[package]] -name = "cipher" -version = "0.3.0" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array 0.14.7", ] [[package]] -name = "cipher" -version = "0.4.4" +name = "blocking" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" dependencies = [ - "crypto-common", - "inout", - "zeroize", + "async-channel 2.3.1", + "async-lock 3.3.0", + "async-task", + "futures-io", + "futures-lite 2.3.0", + "piper", ] [[package]] -name = "clang-sys" -version = "1.6.1" +name = "bounded-collections" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "ca548b6163b872067dc5eb82fd130c56881435e30367d2073594a3d9744120dd" dependencies = [ - "glob", - "libc", - "libloading", + "log", + "parity-scale-codec", + "scale-info", + "serde", ] [[package]] -name = "clap" -version = "4.2.7" +name = "bounded-vec" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", + "thiserror", ] [[package]] -name = "clap_builder" -version = "4.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +name = "bp-header-chain" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "anstream", - "anstyle", - "bitflags", - "clap_lex", - "strsim", + "bp-runtime", + "finality-grandpa", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] -name = "clap_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +name = "bp-messages" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.39", + "bp-header-chain", + "bp-runtime", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-std", ] [[package]] -name = "clap_lex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +name = "bp-parachains" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] [[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +name = "bp-polkadot-core" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "termcolor", - "unicode-width", + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "parity-scale-codec", + "parity-util-mem", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +name = "bp-relayers" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] [[package]] -name = "comfy-table" -version = "6.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7b787b0dc42e8111badfdbe4c3059158ccb2db8780352fa1b01e8ccf45cc4d" +name = "bp-runtime" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "strum", - "strum_macros", - "unicode-width", + "frame-support", + "frame-system", + "hash-db", + "impl-trait-for-tuples", + "log", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "trie-db", ] [[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +name = "bp-test-utils" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "crossbeam-utils", + "bp-header-chain", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "ed25519-dalek", + "finality-grandpa", + "parity-scale-codec", + "sp-application-crypto", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "sp-std", + "sp-trie", ] [[package]] -name = "console" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +name = "bp-xcm-bridge-hub" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.45.0", + "sp-std", ] [[package]] -name = "const-oid" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "constant_time_eq" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +name = "bp-xcm-bridge-hub-router" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] [[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", +name = "bridge-runtime-common" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-relayers", + "bp-runtime", + "bp-xcm-bridge-hub", + "bp-xcm-bridge-hub-router", + "frame-support", + "frame-system", + "hash-db", + "log", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-transaction-payment", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", + "staging-xcm", + "staging-xcm-builder", ] [[package]] -name = "core-foundation-sys" -version = "0.8.4" +name = "bs58" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] -name = "core2" -version = "0.4.0" +name = "bs58" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "memchr", + "tinyvec", ] [[package]] -name = "cpp_demangle" -version = "0.3.5" +name = "bstr" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ - "cfg-if", + "lazy_static", + "memchr", + "regex-automata 0.1.10", ] [[package]] -name = "cpufeatures" -version = "0.2.7" +name = "bstr" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ - "libc", + "memchr", + "serde", ] [[package]] -name = "cranelift-bforest" -version = "0.93.2" +name = "build-helper" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" +checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" dependencies = [ - "cranelift-entity", + "semver 0.6.0", ] [[package]] -name = "cranelift-codegen" -version = "0.93.2" +name = "bumpalo" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" -dependencies = [ - "arrayvec 0.7.4", - "bumpalo", - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-entity", - "cranelift-isle", - "gimli 0.26.2", - "hashbrown 0.12.3", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "cranelift-codegen-meta" -version = "0.93.2" +name = "byte-slice-cast" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" -dependencies = [ - "cranelift-codegen-shared", -] +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] -name = "cranelift-codegen-shared" -version = "0.93.2" +name = "byte-tools" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] -name = "cranelift-entity" -version = "0.93.2" +name = "bytemuck" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" -dependencies = [ - "serde", -] +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[package]] -name = "cranelift-frontend" -version = "0.93.2" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "cranelift-isle" -version = "0.93.2" +name = "bytes" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] -name = "cranelift-native" -version = "0.93.2" +name = "bzip2-sys" +version = "0.1.11+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" dependencies = [ - "cranelift-codegen", + "cc", "libc", - "target-lexicon", + "pkg-config", ] [[package]] -name = "cranelift-wasm" -version = "0.93.2" +name = "c2-chacha" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" +checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80" dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "itertools", - "log", - "smallvec", - "wasmparser", - "wasmtime-types", + "cipher 0.2.5", + "ppv-lite86", ] [[package]] -name = "crc" -version = "3.0.1" +name = "camino" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ - "crc-catalog", + "serde", ] [[package]] -name = "crc-catalog" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" - -[[package]] -name = "crc32fast" -version = "1.3.2" +name = "cargo-platform" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ - "cfg-if", + "serde", ] [[package]] -name = "crossbeam-deque" -version = "0.8.3" +name = "cargo_metadata" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.14" +name = "case" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.8.0", - "scopeguard", -] +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" [[package]] -name = "crossbeam-utils" -version = "0.8.15" +name = "cc" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ - "cfg-if", + "jobserver", + "libc", + "once_cell", ] [[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +name = "ccp-authorities-noting-inherent" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "dc-orchestrator-chain-interface", + "dp-collator-assignment", + "dp-core", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "test-relay-sproof-builder", + "tracing", +] [[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +name = "ccp-xcm" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", + "frame-support", + "frame-system", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-executor", ] [[package]] -name = "crypto-bigint" -version = "0.5.2" +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", + "nom", ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "cfg-expr" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "typenum", + "smallvec", ] [[package]] -name = "crypto-mac" -version = "0.8.0" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array 0.14.7", - "subtle", -] +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "crypto-mac" -version = "0.11.1" +name = "cfg_aliases" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.7", - "subtle", -] +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] -name = "ctor" -version = "0.1.26" +name = "chacha" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" dependencies = [ - "quote", - "syn 1.0.109", + "byteorder", + "keystream", ] [[package]] -name = "ctr" -version = "0.8.0" +name = "chacha20" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ - "cipher 0.3.0", + "cfg-if", + "cipher 0.4.4", + "cpufeatures", ] [[package]] -name = "ctr" -version = "0.9.2" +name = "chacha20poly1305" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ + "aead", + "chacha20", "cipher 0.4.4", -] - -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle", + "poly1305", "zeroize", ] [[package]] -name = "curve25519-dalek" -version = "3.2.0" +name = "chrono" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.5", ] [[package]] -name = "curve25519-dalek" -version = "4.1.1" +name = "cid" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "b9b68e3193982cd54187d71afdb2a271ad4cf8af157858e9cb911b91321de143" dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "fiat-crypto", - "platforms 3.0.2", - "rustc_version 0.4.0", - "subtle", - "zeroize", + "core2", + "multibase", + "multihash 0.17.0", + "serde", + "unsigned-varint", ] [[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" +name = "cipher" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", + "generic-array 0.14.7", ] [[package]] -name = "cxx" -version = "1.0.94" +name = "cipher" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", + "crypto-common", + "inout", + "zeroize", ] [[package]] -name = "cxx-build" -version = "1.0.94" +name = "ckb-merkle-mountain-range" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.39", + "cfg-if", ] [[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" +name = "clang-sys" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", + "glob", + "libc", + "libloading", ] [[package]] -name = "darling" -version = "0.14.4" +name = "clap" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ - "darling_core", - "darling_macro", + "clap_builder", + "clap_derive", ] [[package]] -name = "darling_core" -version = "0.14.4" +name = "clap_builder" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", + "anstream", + "anstyle", + "clap_lex", "strsim", - "syn 1.0.109", + "terminal_size", ] [[package]] -name = "darling_macro" -version = "0.14.4" +name = "clap_derive" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "darling_core", + "heck 0.5.0", + "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.65", ] [[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "data-encoding-macro" -version = "0.1.12" +name = "clap_lex" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" -dependencies = [ - "data-encoding", - "data-encoding-macro-internal", -] +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] -name = "data-encoding-macro-internal" -version = "0.1.10" +name = "coarsetime" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" -dependencies = [ - "data-encoding", - "syn 1.0.109", -] - -[[package]] -name = "department-funding" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-support-test", - "frame-system", - "pallet-balances", - "pallet-support", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "schelling-game-shared", - "schelling-game-shared-link", - "shared-storage", - "shared-storage-link", - "sortition-sum-game", - "sp-core", - "sp-io", - "sp-runtime", -] - -[[package]] -name = "department-funding-rpc" -version = "0.1.0" -dependencies = [ - "department-funding-runtime-api", - "jsonrpsee", - "sc-rpc", - "sc-rpc-api", - "sp-api", - "sp-blockchain", - "sp-runtime", -] - -[[package]] -name = "department-funding-runtime-api" -version = "0.1.0" +checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" dependencies = [ - "frame-support", - "sp-api", + "libc", + "wasix", + "wasm-bindgen", ] [[package]] -name = "der" -version = "0.6.1" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", + "termcolor", + "unicode-width", ] [[package]] -name = "der" -version = "0.7.5" +name = "colorchoice" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" -dependencies = [ - "const-oid", - "zeroize", -] +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] -name = "der-parser" -version = "7.0.0" +name = "comfy-table" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ - "asn1-rs 0.3.1", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", + "strum 0.26.2", + "strum_macros 0.26.2", + "unicode-width", ] [[package]] -name = "der-parser" -version = "8.2.0" +name = "common-path" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" -dependencies = [ - "asn1-rs 0.5.2", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] +checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" [[package]] -name = "derive-syn-parse" -version = "0.1.5" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "crossbeam-utils", ] [[package]] -name = "derive_builder" -version = "0.11.2" +name = "console" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ - "derive_builder_macro", + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", ] [[package]] -name = "derive_builder_core" -version = "0.11.2" +name = "const-oid" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "derive_builder_macro" -version = "0.11.2" +name = "const-random" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ - "derive_builder_core", - "syn 1.0.109", + "const-random-macro", ] [[package]] -name = "derive_more" -version = "0.99.17" +name = "const-random-macro" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "getrandom 0.2.15", + "once_cell", + "tiny-keccak", ] [[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "difflib" -version = "0.4.0" +name = "constant_time_eq" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] -name = "digest" -version = "0.8.1" +name = "constant_time_eq" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] -name = "digest" -version = "0.9.0" +name = "constcat" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" [[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +name = "container-chain-frontier-node" +version = "0.7.0" dependencies = [ - "block-buffer 0.10.4", - "const-oid", - "crypto-common", - "subtle", -] + "async-io 1.13.0", + "async-trait", + "ccp-authorities-noting-inherent", + "clap", + "container-chain-template-frontier-runtime", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-parachain-inherent", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-test-relay-sproof-builder", + "fc-api", + "fc-cli", + "fc-consensus", + "fc-db", + "fc-mapping-sync", + "fc-rpc", + "fc-rpc-core", + "fc-storage", + "flume 0.10.14", + "fp-evm", + "fp-rpc", + "frame-benchmarking", + "frame-benchmarking-cli", + "frame-system-rpc-runtime-api", + "futures 0.3.30", + "hex-literal 0.3.4", + "jsonrpsee", + "log", + "manual-xcm-rpc", + "nimbus-consensus", + "nimbus-primitives", + "node-common", + "pallet-ethereum", + "pallet-transaction-payment-rpc", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "polkadot-cli", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-service", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-manual-seal", + "sc-executor", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-offchain", + "sc-rpc", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "serde_json", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-debug-derive", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-timestamp", + "sp-transaction-pool", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", + "tc-consensus", + "try-runtime-cli", + "url", +] [[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +name = "container-chain-simple-node" +version = "0.7.0" dependencies = [ - "dirs-sys", + "async-io 1.13.0", + "async-trait", + "ccp-authorities-noting-inherent", + "clap", + "container-chain-template-simple-runtime", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-parachain-inherent", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "dc-orchestrator-chain-interface", + "dc-orchestrator-chain-rpc-interface", + "dp-core", + "flume 0.10.14", + "frame-benchmarking", + "frame-benchmarking-cli", + "futures 0.3.30", + "hex-literal 0.3.4", + "jsonrpsee", + "log", + "manual-xcm-rpc", + "nimbus-consensus", + "nimbus-primitives", + "node-common", + "pallet-shared-storage", + "parity-scale-codec", + "polkadot-cli", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-service", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-manual-seal", + "sc-executor", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-offchain", + "sc-rpc", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "serde_json", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-timestamp", + "sp-transaction-pool", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", + "tc-consensus", + "tokio", + "try-runtime-cli", + "url", ] [[package]] -name = "directories-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +name = "container-chain-template-frontier-runtime" +version = "0.1.0" dependencies = [ - "cfg-if", - "dirs-sys-next", + "async-backing-primitives", + "ccp-xcm", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "dp-consensus", + "dp-impl-tanssi-pallets-config", + "dp-slot-duration-runtime-api", + "fp-account", + "fp-evm", + "fp-rpc", + "fp-self-contained", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "nimbus-primitives", + "num_enum", + "pallet-asset-rate", + "pallet-assets", + "pallet-async-backing", + "pallet-author-inherent", + "pallet-balances", + "pallet-base-fee", + "pallet-cc-authorities-noting", + "pallet-ethereum", + "pallet-evm", + "pallet-evm-chain-id", + "pallet-evm-precompile-balances-erc20", + "pallet-evm-precompile-batch", + "pallet-evm-precompile-call-permit", + "pallet-evm-precompile-modexp", + "pallet-evm-precompile-sha3fips", + "pallet-evm-precompile-simple", + "pallet-evm-precompile-xcm-utils", + "pallet-evm-precompileset-assets-erc20", + "pallet-foreign-asset-creator", + "pallet-maintenance-mode", + "pallet-message-queue", + "pallet-migrations", + "pallet-multisig", + "pallet-proxy", + "pallet-root-testing", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-tx-pause", + "pallet-utility", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-executor-utils", + "parachains-common", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "precompile-utils", + "runtime-common", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-trie", + "sp-version", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", + "xcm-primitives", ] [[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +name = "container-chain-template-simple-runtime" +version = "0.1.0" dependencies = [ - "libc", - "redox_users", - "winapi", + "async-backing-primitives", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "department-funding-runtime-api", + "dp-consensus", + "dp-impl-tanssi-pallets-config", + "dp-slot-duration-runtime-api", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "nimbus-primitives", + "pallet-asset-rate", + "pallet-assets", + "pallet-async-backing", + "pallet-author-inherent", + "pallet-balances", + "pallet-cc-authorities-noting", + "pallet-department-funding", + "pallet-foreign-asset-creator", + "pallet-insecure-randomness-collective-flip", + "pallet-maintenance-mode", + "pallet-message-queue", + "pallet-migrations", + "pallet-multisig", + "pallet-positive-externality", + "pallet-profile-validation", + "pallet-project-tips", + "pallet-proxy", + "pallet-root-testing", + "pallet-schelling-game-shared", + "pallet-session", + "pallet-shared-storage", + "pallet-sortition-sum-game", + "pallet-sudo", + "pallet-template", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-tx-pause", + "pallet-utility", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-executor-utils", + "parachains-common", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "positive-externality-runtime-api", + "profile-validation-runtime-api", + "project-tips-runtime-api", + "runtime-common", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-trie", + "sp-version", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", + "xcm-primitives", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "convert_case" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] -name = "displaydoc" -version = "0.2.4" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", + "core-foundation-sys", + "libc", ] [[package]] -name = "dissimilar" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" - -[[package]] -name = "downcast" -version = "0.11.0" +name = "core-foundation-sys" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] -name = "downcast-rs" -version = "1.2.0" +name = "core2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] [[package]] -name = "dtoa" -version = "1.0.6" +name = "core_extensions" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" +checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee" [[package]] -name = "dyn-clonable" -version = "0.9.0" +name = "cpp_demangle" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" dependencies = [ - "dyn-clonable-impl", - "dyn-clone", + "cfg-if", ] [[package]] -name = "dyn-clonable-impl" -version = "0.9.0" +name = "cpu-time" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "libc", + "winapi", ] [[package]] -name = "dyn-clone" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" - -[[package]] -name = "ecdsa" -version = "0.14.8" +name = "cpufeatures" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", + "libc", ] [[package]] -name = "ecdsa" -version = "0.16.6" +name = "cranelift-bforest" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70" dependencies = [ - "der 0.7.5", - "digest 0.10.7", - "elliptic-curve 0.13.4", - "rfc6979 0.4.0", - "signature 2.1.0", + "cranelift-entity", ] [[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature 1.6.4", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" +name = "cranelift-codegen" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220" dependencies = [ - "curve25519-dalek 3.2.0", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli 0.27.3", + "hashbrown 0.13.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", ] [[package]] -name = "ed25519-zebra" -version = "3.1.0" +name = "cranelift-codegen-meta" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da" dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", - "hex", - "rand_core 0.6.4", - "sha2 0.9.9", - "zeroize", + "cranelift-codegen-shared", ] [[package]] -name = "either" -version = "1.9.0" +name = "cranelift-codegen-shared" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8" [[package]] -name = "elliptic-curve" -version = "0.12.3" +name = "cranelift-entity" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.7", - "ff 0.12.1", - "generic-array 0.14.7", - "group 0.12.1", - "hkdf", - "pem-rfc7468", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", + "serde", ] [[package]] -name = "elliptic-curve" -version = "0.13.4" +name = "cranelift-frontend" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.2", - "digest 0.10.7", - "ff 0.13.0", - "generic-array 0.14.7", - "group 0.13.0", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sec1 0.7.3", - "subtle", - "zeroize", + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", ] [[package]] -name = "encode_unicode" -version = "0.3.6" +name = "cranelift-isle" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba" [[package]] -name = "enum-as-inner" -version = "0.5.1" +name = "cranelift-native" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", + "cranelift-codegen", + "libc", + "target-lexicon", ] [[package]] -name = "env_logger" -version = "0.10.0" +name = "cranelift-wasm" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "ff3220489a3d928ad91e59dd7aeaa8b3de18afb554a6211213673a71c90737ac" dependencies = [ - "humantime", - "is-terminal", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools 0.10.5", "log", - "regex", - "termcolor", -] - -[[package]] -name = "environmental" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", + "smallvec", + "wasmparser", + "wasmtime-types", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "crc" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ - "cc", - "libc", + "crc-catalog", ] [[package]] -name = "event-listener" -version = "2.5.3" +name = "crc-catalog" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] -name = "exit-future" -version = "0.2.0" +name = "crc32fast" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "futures", + "cfg-if", ] [[package]] -name = "expander" -version = "1.0.0" +name = "crossbeam-deque" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "blake2", - "fs-err", - "proc-macro2", - "quote", - "syn 1.0.109", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fallible-iterator" -version = "0.2.0" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] [[package]] -name = "fastrand" -version = "1.9.0" +name = "crossbeam-queue" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "instant", + "crossbeam-utils", ] [[package]] -name = "fdlimit" -version = "0.2.1" +name = "crossbeam-utils" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" -dependencies = [ - "libc", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] -name = "ff" -version = "0.12.1" +name = "crunchy" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] -name = "ff" -version = "0.13.0" +name = "crypto-bigint" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ + "generic-array 0.14.7", "rand_core 0.6.4", - "subtle", + "subtle 2.5.0", + "zeroize", ] [[package]] -name = "fiat-crypto" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f69037fe1b785e84986b4f2cbcf647381876a00671d25ceef715d7812dd7e1dd" - -[[package]] -name = "file-per-thread-logger" +name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "env_logger", - "log", + "generic-array 0.14.7", + "rand_core 0.6.4", + "typenum", ] [[package]] -name = "filetime" -version = "0.2.21" +name = "crypto-mac" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "windows-sys 0.48.0", + "generic-array 0.12.4", + "subtle 1.0.0", ] [[package]] -name = "finality-grandpa" -version = "0.16.2" +name = "crypto-mac" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "either", - "futures", - "futures-timer", - "log", - "num-traits", - "parity-scale-codec", - "parking_lot 0.12.1", - "scale-info", + "generic-array 0.14.7", + "subtle 2.5.0", ] [[package]] -name = "fixed-hash" -version = "0.8.0" +name = "crypto-mac" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", + "generic-array 0.14.7", + "subtle 2.5.0", ] [[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.26" +name = "ctr" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "crc32fast", - "libz-sys", - "miniz_oxide 0.7.1", + "cipher 0.4.4", ] [[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +name = "cumulus-client-cli" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "num-traits", + "clap", + "parity-scale-codec", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-service", + "sp-blockchain", + "sp-core", + "sp-runtime", + "url", ] [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "fork-tree" -version = "3.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-client-collator" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-primitives-core", + "futures 0.3.30", "parity-scale-codec", + "parking_lot 0.12.2", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-consensus", + "sp-core", + "sp-runtime", + "tracing", ] [[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +name = "cumulus-client-consensus-aura" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - -[[package]] -name = "frame-benchmarking" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-support-procedural", - "frame-system", - "linregress", - "log", + "async-trait", + "cumulus-client-collator", + "cumulus-client-consensus-common", + "cumulus-client-consensus-proposer", + "cumulus-client-parachain-inherent", + "cumulus-primitives-aura", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures 0.3.30", "parity-scale-codec", - "paste", - "scale-info", - "serde", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-babe", + "sc-consensus-slots", + "sc-telemetry", + "schnellru", "sp-api", "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", "sp-core", - "sp-io", + "sp-inherents", + "sp-keystore", "sp-runtime", - "sp-runtime-interface", - "sp-std", - "sp-storage", - "static_assertions", + "sp-state-machine", + "sp-timestamp", + "substrate-prometheus-endpoint", + "tracing", ] [[package]] -name = "frame-benchmarking-cli" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-client-consensus-common" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "Inflector", - "array-bytes", - "chrono", - "clap", - "comfy-table", - "frame-benchmarking", - "frame-support", - "frame-system", - "gethostname", - "handlebars", - "itertools", - "lazy_static", - "linked-hash-map", + "async-trait", + "cumulus-client-pov-recovery", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "dyn-clone", + "futures 0.3.30", "log", "parity-scale-codec", - "rand 0.8.5", - "rand_pcg", - "sc-block-builder", - "sc-cli", + "polkadot-primitives", "sc-client-api", - "sc-client-db", - "sc-executor", - "sc-service", - "sc-sysinfo", - "serde", - "serde_json", - "sp-api", + "sc-consensus", + "sc-consensus-babe", + "schnellru", "sp-blockchain", + "sp-consensus", + "sp-consensus-slots", "sp-core", - "sp-database", - "sp-externalities", + "sp-runtime", + "sp-timestamp", + "sp-trie", + "substrate-prometheus-endpoint", + "tracing", +] + +[[package]] +name = "cumulus-client-consensus-proposer" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "anyhow", + "async-trait", + "cumulus-primitives-parachain-inherent", + "sp-consensus", "sp-inherents", - "sp-keystore", "sp-runtime", "sp-state-machine", - "sp-std", - "sp-storage", - "sp-trie", "thiserror", - "thousands", ] [[package]] -name = "frame-executive" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-client-network" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-support", - "frame-system", - "frame-try-runtime", + "async-trait", + "cumulus-relay-chain-interface", + "futures 0.3.30", + "futures-timer", + "parity-scale-codec", + "parking_lot 0.12.2", + "polkadot-node-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "sc-client-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "tracing", +] + +[[package]] +name = "cumulus-client-parachain-inherent" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "cumulus-test-relay-sproof-builder", "parity-scale-codec", + "sc-client-api", "scale-info", + "sp-api", "sp-core", - "sp-io", + "sp-inherents", "sp-runtime", + "sp-state-machine", "sp-std", - "sp-tracing", + "sp-storage", + "sp-trie", + "tracing", ] [[package]] -name = "frame-metadata" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +name = "cumulus-client-pov-recovery" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "cfg-if", + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures 0.3.30", + "futures-timer", "parity-scale-codec", - "scale-info", - "serde", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "rand", + "sc-client-api", + "sc-consensus", + "sp-consensus", + "sp-maybe-compressed-blob", + "sp-runtime", + "tracing", ] [[package]] -name = "frame-remote-externalities" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-client-service" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "cumulus-client-cli", + "cumulus-client-collator", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-pov-recovery", + "cumulus-primitives-core", + "cumulus-primitives-proof-size-hostfunction", + "cumulus-relay-chain-inprocess-interface", + "cumulus-relay-chain-interface", + "cumulus-relay-chain-minimal-node", + "futures 0.3.30", + "polkadot-primitives", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-sync", + "sc-network-transactions", + "sc-rpc", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-transaction-pool", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-transaction-pool", +] + +[[package]] +name = "cumulus-pallet-dmp-queue" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "async-recursion", - "futures", - "indicatif", - "jsonrpsee", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", "log", "parity-scale-codec", - "serde", - "sp-core", + "scale-info", "sp-io", "sp-runtime", - "spinners", - "substrate-rpc-client", - "tokio", + "sp-std", + "staging-xcm", ] [[package]] -name = "frame-support" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-pallet-parachain-system" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "bitflags", + "bytes", + "cumulus-pallet-parachain-system-proc-macro", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-primitives-proof-size-hostfunction", "environmental", - "frame-metadata", - "frame-support-procedural", + "frame-benchmarking", + "frame-support", + "frame-system", "impl-trait-for-tuples", - "k256", "log", - "once_cell", + "pallet-message-queue", "parity-scale-codec", - "paste", + "polkadot-parachain-primitives", + "polkadot-runtime-parachains", "scale-info", - "serde", - "smallvec", - "sp-api", - "sp-arithmetic", "sp-core", - "sp-core-hashing-proc-macro", + "sp-externalities", "sp-inherents", "sp-io", "sp-runtime", - "sp-staking", "sp-state-machine", "sp-std", - "sp-tracing", - "sp-weights", - "tt-call", -] - -[[package]] -name = "frame-support-procedural" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "Inflector", - "cfg-expr", - "derive-syn-parse", - "frame-support-procedural-tools", - "itertools", - "proc-macro-warning", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "frame-support-procedural-tools" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support-procedural-tools-derive", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.39", + "sp-trie", + "sp-version", + "staging-xcm", + "trie-db", ] [[package]] -name = "frame-support-procedural-tools-derive" -version = "3.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-pallet-parachain-system-proc-macro" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] -name = "frame-support-test" +name = "cumulus-pallet-session-benchmarking" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "frame-benchmarking", "frame-support", - "frame-support-test-pallet", "frame-system", + "pallet-session", "parity-scale-codec", - "pretty_assertions", - "rustversion", - "scale-info", - "serde", - "sp-api", - "sp-arithmetic", - "sp-core", - "sp-io", "sp-runtime", - "sp-state-machine", "sp-std", - "sp-version", - "trybuild", ] [[package]] -name = "frame-support-test-pallet" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-pallet-xcm" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "cumulus-primitives-core", "frame-support", "frame-system", "parity-scale-codec", "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", ] [[package]] -name = "frame-system" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-pallet-xcmp-queue" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "bounded-collections", + "bp-xcm-bridge-hub-router", + "cumulus-primitives-core", + "frame-benchmarking", "frame-support", + "frame-system", "log", + "pallet-message-queue", "parity-scale-codec", + "polkadot-runtime-common", + "polkadot-runtime-parachains", "scale-info", - "serde", "sp-core", "sp-io", "sp-runtime", "sp-std", - "sp-version", - "sp-weights", + "staging-xcm", + "staging-xcm-executor", ] [[package]] -name = "frame-system-benchmarking" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-primitives-aura" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", "parity-scale-codec", - "scale-info", - "sp-core", + "polkadot-core-primitives", + "polkadot-primitives", + "sp-api", + "sp-consensus-aura", "sp-runtime", "sp-std", ] [[package]] -name = "frame-system-rpc-runtime-api" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-primitives-core" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "scale-info", "sp-api", + "sp-runtime", + "sp-std", + "sp-trie", + "staging-xcm", ] [[package]] -name = "frame-try-runtime" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "cumulus-primitives-parachain-inherent" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-support", + "async-trait", + "cumulus-primitives-core", "parity-scale-codec", - "sp-api", - "sp-runtime", + "scale-info", + "sp-core", + "sp-inherents", "sp-std", + "sp-trie", ] [[package]] -name = "fs-err" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" +name = "cumulus-primitives-proof-size-hostfunction" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "sp-externalities", + "sp-runtime-interface", + "sp-trie", +] [[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +name = "cumulus-primitives-timestamp" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "libc", - "winapi", + "cumulus-primitives-core", + "futures 0.3.30", + "parity-scale-codec", + "sp-inherents", + "sp-std", + "sp-timestamp", ] [[package]] -name = "fs4" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f5b6908aecca5812a4569056285e58c666588c9573ee59765bf1d3692699e2" +name = "cumulus-primitives-utility" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "rustix 0.37.19", - "windows-sys 0.48.0", + "cumulus-primitives-core", + "frame-support", + "log", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +name = "cumulus-relay-chain-inprocess-interface" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures 0.3.30", + "futures-timer", + "polkadot-cli", + "polkadot-service", + "sc-cli", + "sc-client-api", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sp-api", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", +] [[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +name = "cumulus-relay-chain-interface" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "async-trait", + "cumulus-primitives-core", + "futures 0.3.30", + "jsonrpsee-core", + "parity-scale-codec", + "polkadot-overseer", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-state-machine", + "thiserror", ] [[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +name = "cumulus-relay-chain-minimal-node" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures-core", - "futures-sink", + "array-bytes 6.2.3", + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-relay-chain-rpc-interface", + "futures 0.3.30", + "parking_lot 0.12.2", + "polkadot-availability-recovery", + "polkadot-collator-protocol", + "polkadot-core-primitives", + "polkadot-network-bridge", + "polkadot-node-collation-generation", + "polkadot-node-core-chain-api", + "polkadot-node-core-prospective-parachains", + "polkadot-node-core-runtime-api", + "polkadot-node-network-protocol", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sc-authority-discovery", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-service", + "sc-tracing", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-runtime", + "substrate-prometheus-endpoint", + "tokio", + "tracing", ] [[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +name = "cumulus-relay-chain-rpc-interface" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "either", + "futures 0.3.30", + "futures-timer", + "jsonrpsee", + "parity-scale-codec", + "pin-project", + "polkadot-overseer", + "rand", + "sc-client-api", + "sc-rpc-api", + "sc-service", + "schnellru", + "serde", + "serde_json", + "smoldot", + "smoldot-light", + "sp-api", + "sp-authority-discovery", + "sp-consensus-babe", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "sp-version", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "url", +] [[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +name = "cumulus-test-relay-sproof-builder" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures-core", - "futures-task", - "futures-util", - "num_cpus", + "cumulus-primitives-core", + "parity-scale-codec", + "polkadot-primitives", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", ] [[package]] -name = "futures-io" -version = "0.3.28" +name = "curve25519-dalek" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle 2.5.0", + "zeroize", +] [[package]] -name = "futures-lite" -version = "1.13.0" +name = "curve25519-dalek" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite 0.2.9", - "waker-fn", + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version 0.4.0", + "subtle 2.5.0", + "zeroize", ] [[package]] -name = "futures-macro" -version = "0.3.28" +name = "curve25519-dalek-derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] -name = "futures-rustls" -version = "0.22.2" +name = "curve25519-dalek-ng" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" dependencies = [ - "futures-io", - "rustls 0.20.8", - "webpki 0.22.0", + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", ] [[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - -[[package]] -name = "futures-util" -version = "0.3.28" +name = "cxx" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "bb497fad022245b29c2a0351df572e2d67c1046bcef2260ebc022aec81efea82" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite 0.2.9", - "pin-utils", - "slab", + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", ] [[package]] -name = "fxhash" -version = "0.2.1" +name = "cxx-build" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +checksum = "9327c7f9fbd6329a200a5d4aa6f674c60ab256525ff0084b52a889d4e4c60cee" dependencies = [ - "byteorder", + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.65", ] [[package]] -name = "generic-array" -version = "0.12.4" +name = "cxxbridge-flags" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] +checksum = "688c799a4a846f1c0acb9f36bb9c6272d9b3d9457f3633c7753c6057270df13c" [[package]] -name = "generic-array" -version = "0.14.7" +name = "cxxbridge-macro" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "928bc249a7e3cd554fd2e8e08a426e9670c50bbfc9a621653cfa9accc9641783" dependencies = [ - "typenum", - "version_check", - "zeroize", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +name = "dancebox-runtime" +version = "0.1.0" dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", + "assets-common", + "async-backing-primitives", + "container-chain-template-frontier-runtime", + "container-chain-template-simple-runtime", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "cumulus-test-relay-sproof-builder", + "dp-consensus", + "dp-core", + "dp-slot-duration-runtime-api", + "emulated-integration-tests-common", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "nimbus-primitives", + "pallet-asset-rate", + "pallet-assets", + "pallet-async-backing", + "pallet-author-inherent", + "pallet-author-noting", + "pallet-author-noting-runtime-api", + "pallet-authority-assignment", + "pallet-authority-mapping", + "pallet-balances", + "pallet-collator-assignment", + "pallet-collator-assignment-runtime-api", + "pallet-configuration", + "pallet-data-preservers", + "pallet-foreign-asset-creator", + "pallet-identity", + "pallet-inflation-rewards", + "pallet-initializer", + "pallet-invulnerables", + "pallet-maintenance-mode", + "pallet-message-queue", + "pallet-migrations", + "pallet-multisig", + "pallet-pooled-staking", + "pallet-proxy", + "pallet-registrar", + "pallet-registrar-runtime-api", + "pallet-relay-storage-roots", + "pallet-root-testing", + "pallet-services-payment", + "pallet-services-payment-runtime-api", + "pallet-session", + "pallet-staking", + "pallet-stream-payment", + "pallet-stream-payment-runtime-api", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-tx-pause", + "pallet-utility", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-core-buyer", + "parachains-common", + "parity-scale-codec", + "paste", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "polkadot-service", + "rococo-runtime", + "rococo-runtime-constants", + "runtime-common", + "sc-consensus-grandpa", + "sc-service", + "scale-info", + "serde", + "serde_json", + "smallvec", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-slots", + "sp-core", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-trie", + "sp-version", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", + "tanssi-relay-encoder", + "test-relay-sproof-builder", + "tp-author-noting-inherent", + "tp-traits", + "westend-runtime", + "westend-runtime-constants", + "xcm-emulator", + "xcm-primitives", ] [[package]] -name = "getrandom" -version = "0.2.11" +name = "data-encoding" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] -name = "ghash" -version = "0.4.4" +name = "data-encoding-macro" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" dependencies = [ - "opaque-debug 0.3.0", - "polyval 0.5.3", + "data-encoding", + "data-encoding-macro-internal", ] [[package]] -name = "ghash" -version = "0.5.0" +name = "data-encoding-macro-internal" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" dependencies = [ - "opaque-debug 0.3.0", - "polyval 0.6.0", + "data-encoding", + "syn 1.0.109", ] [[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +name = "dc-orchestrator-chain-interface" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", + "async-trait", + "cumulus-primitives-core", + "dp-core", + "futures 0.3.30", + "jsonrpsee", + "parity-scale-codec", + "polkadot-overseer", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-state-machine", + "thiserror", ] [[package]] -name = "gimli" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +name = "dc-orchestrator-chain-rpc-interface" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" +dependencies = [ + "async-io 1.13.0", + "async-trait", + "dc-orchestrator-chain-interface", + "dp-core", + "futures 0.3.30", + "jsonrpsee", + "parity-scale-codec", + "polkadot-overseer", + "sc-client-api", + "sc-rpc-api", + "sc-service", + "schnellru", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-state-machine", + "sp-storage", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] [[package]] -name = "globset" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +name = "department-funding-runtime-api" +version = "0.1.0" dependencies = [ - "aho-corasick 0.7.20", - "bstr", - "fnv", - "log", - "regex", + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-std", ] [[package]] -name = "group" -version = "0.12.1" +name = "der" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", - "subtle", + "const-oid", + "zeroize", ] [[package]] -name = "group" -version = "0.13.0" +name = "der-parser" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ - "ff 0.13.0", - "rand_core 0.6.4", - "subtle", + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", ] [[package]] -name = "h2" -version = "0.3.18" +name = "deranged" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", + "powerfmt", ] [[package]] -name = "handlebars" -version = "4.3.6" +name = "derivative" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "log", - "pest", - "pest_derive", - "serde", - "serde_json", - "thiserror", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "hash-db" -version = "0.16.0" +name = "derive-syn-parse" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "hash256-std-hasher" -version = "0.15.2" +name = "derive-syn-parse" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ - "crunchy", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "ahash 0.7.6", + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn 1.0.109", ] [[package]] -name = "hashbrown" -version = "0.13.2" +name = "diff" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.7", -] +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] -name = "heck" -version = "0.4.1" +name = "difflib" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "digest" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "libc", + "generic-array 0.12.4", ] [[package]] -name = "hermit-abi" -version = "0.2.6" +name = "digest" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "libc", + "generic-array 0.14.7", ] [[package]] -name = "hermit-abi" -version = "0.3.1" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle 2.5.0", +] [[package]] -name = "hex" -version = "0.4.3" +name = "directories" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] [[package]] -name = "hex-literal" -version = "0.4.1" +name = "directories-next" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] [[package]] -name = "hkdf" -version = "0.12.3" +name = "dirs-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ - "hmac 0.12.1", + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", ] [[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac 0.8.0", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.11.0" +name = "dirs-sys-next" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ - "crypto-mac 0.11.1", - "digest 0.9.0", + "libc", + "redox_users", + "winapi", ] [[package]] -name = "hmac" -version = "0.12.1" +name = "displaydoc" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "digest 0.10.7", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "hmac-drbg" -version = "0.3.0" +name = "dissimilar" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array 0.14.7", - "hmac 0.8.1", -] +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] -name = "hostname" -version = "0.3.1" +name = "docify" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "43a2f138ad521dc4a2ced1a4576148a6a610b4c5923933b062a263130a6802ce" dependencies = [ - "libc", - "match_cfg", - "winapi", + "docify_macros", ] [[package]] -name = "http" -version = "0.2.9" +name = "docify_macros" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "1a081e51fb188742f5a7a1164ad752121abcb22874b21e2c3b0dd040c515fdad" dependencies = [ - "bytes", - "fnv", - "itoa", + "common-path", + "derive-syn-parse 0.2.0", + "once_cell", + "proc-macro2", + "quote", + "regex", + "syn 2.0.65", + "termcolor", + "toml 0.8.13", + "walkdir", ] [[package]] -name = "http-body" -version = "0.4.5" +name = "dotenvy" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite 0.2.9", -] +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] -name = "http-range-header" -version = "0.3.0" +name = "downcast" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] -name = "httparse" -version = "1.8.0" +name = "downcast-rs" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +name = "dp-chain-state-snapshot" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" +dependencies = [ + "cumulus-primitives-core", + "parity-scale-codec", + "sp-runtime", + "sp-state-machine", + "sp-trie", +] [[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +name = "dp-collator-assignment" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "hex-literal 0.3.4", + "log", + "parity-scale-codec", + "polkadot-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] [[package]] -name = "hyper" -version = "0.14.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +name = "dp-consensus" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite 0.2.9", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-consensus-aura", + "sp-runtime", + "sp-std", ] [[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +name = "dp-core" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "http", - "hyper", - "log", - "rustls 0.20.8", - "rustls-native-certs", - "tokio", - "tokio-rustls", - "webpki-roots", + "cumulus-primitives-core", + "frame-support", + "hex-literal 0.3.4", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "iana-time-zone" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +name = "dp-impl-tanssi-pallets-config" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows 0.48.0", + "dp-consensus", + "frame-support", + "impls", + "pallet-author-inherent", + "pallet-cc-authorities-noting", + "pallet-timestamp", + "sp-core", + "sp-runtime", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +name = "dp-slot-duration-runtime-api" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "cxx", - "cxx-build", + "cumulus-primitives-core", + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "ident_case" -version = "1.0.1" +name = "dtoa" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] -name = "idna" -version = "0.2.3" +name = "dyn-clonable" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "dyn-clonable-impl", + "dyn-clone", ] [[package]] -name = "idna" -version = "0.4.0" +name = "dyn-clonable-impl" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "if-addrs" -version = "0.7.0" +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "libc", - "winapi", + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] -name = "if-watch" -version = "3.0.1" +name = "ed25519" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "async-io", - "core-foundation", - "fnv", - "futures", - "if-addrs", - "ipnet", - "log", - "rtnetlink", - "system-configuration", - "tokio", - "windows 0.34.0", + "pkcs8", + "signature", ] [[package]] -name = "impl-codec" -version = "0.6.0" +name = "ed25519-dalek" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "parity-scale-codec", + "curve25519-dalek 4.1.2", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "subtle 2.5.0", + "zeroize", ] [[package]] -name = "impl-serde" -version = "0.4.0" +name = "ed25519-zebra" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ - "serde", + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", ] [[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" +name = "ed25519-zebra" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "curve25519-dalek 4.1.2", + "ed25519", + "hashbrown 0.14.5", + "hex", + "rand_core 0.6.4", + "sha2 0.10.8", + "zeroize", ] [[package]] -name = "indexmap" -version = "1.9.3" +name = "either" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" dependencies = [ - "autocfg", - "hashbrown 0.12.3", "serde", ] [[package]] -name = "indicatif" -version = "0.17.5" +name = "elliptic-curve" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array 0.14.7", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle 2.5.0", + "zeroize", ] [[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.7", +name = "emulated-integration-tests-common" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "asset-test-utils", + "bp-messages", + "bridge-runtime-common", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "frame-support", + "pallet-assets", + "pallet-balances", + "pallet-bridge-messages", + "pallet-im-online", + "pallet-message-queue", + "pallet-xcm", + "parachains-common", + "parity-scale-codec", + "paste", + "polkadot-primitives", + "polkadot-runtime-parachains", + "polkadot-service", + "sc-consensus-grandpa", + "serde_json", + "sp-authority-discovery", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-runtime", + "staging-xcm", + "xcm-emulator", ] [[package]] -name = "instant" -version = "0.1.12" +name = "encode_unicode" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "integer-sqrt" -version = "0.1.5" +name = "enum-as-inner" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ - "num-traits", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "interceptor" -version = "0.8.2" +name = "enumflags2" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8a11ae2da61704edada656798b61c94b35ecac2c58eb955156987d5e6be90b" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" dependencies = [ - "async-trait", - "bytes", - "log", - "rand 0.8.5", - "rtcp", - "rtp", - "thiserror", - "tokio", - "waitgroup", - "webrtc-srtp", - "webrtc-util", + "enumflags2_derive", ] [[package]] -name = "io-lifetimes" -version = "1.0.10" +name = "enumflags2_derive" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "ip_network" -version = "0.4.1" +name = "enumn" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] [[package]] -name = "ipconfig" -version = "0.3.1" +name = "env_logger" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ - "socket2", - "widestring", - "winapi", - "winreg", + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", ] [[package]] -name = "ipnet" -version = "2.7.2" +name = "environmental" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" [[package]] -name = "is-terminal" -version = "0.4.7" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix 0.37.19", - "windows-sys 0.48.0", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "itertools" -version = "0.10.5" +name = "errno" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "either", + "libc", + "windows-sys 0.52.0", ] [[package]] -name = "itoa" -version = "1.0.6" +name = "ethbloom" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] [[package]] -name = "jobserver" -version = "0.1.26" +name = "ethereum" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "2e04d24d20b8ff2235cffbf242d5092de3aa45f77c5270ddbfadd2778ca13fea" dependencies = [ - "libc", + "bytes", + "ethereum-types", + "hash-db", + "hash256-std-hasher", + "parity-scale-codec", + "rlp", + "scale-info", + "serde", + "sha3", + "trie-root", ] [[package]] -name = "js-sys" -version = "0.3.61" +name = "ethereum-types" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ - "wasm-bindgen", + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types", + "scale-info", + "uint", ] [[package]] -name = "jsonrpsee" -version = "0.16.2" +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" dependencies = [ - "jsonrpsee-core", - "jsonrpsee-http-client", - "jsonrpsee-proc-macros", - "jsonrpsee-server", - "jsonrpsee-types", - "jsonrpsee-ws-client", - "tracing", + "concurrent-queue", + "parking", + "pin-project-lite 0.2.14", ] [[package]] -name = "jsonrpsee-client-transport" -version = "0.16.2" +name = "event-listener" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ - "futures-util", - "http", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "rustls-native-certs", - "soketto", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "webpki-roots", + "concurrent-queue", + "parking", + "pin-project-lite 0.2.14", ] [[package]] -name = "jsonrpsee-core" -version = "0.16.2" +name = "event-listener" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ - "anyhow", - "arrayvec 0.7.4", - "async-lock", - "async-trait", - "beef", - "futures-channel", - "futures-timer", - "futures-util", - "globset", - "hyper", - "jsonrpsee-types", - "parking_lot 0.12.1", - "rand 0.8.5", - "rustc-hash", - "serde", - "serde_json", - "soketto", - "thiserror", - "tokio", - "tracing", + "concurrent-queue", + "parking", + "pin-project-lite 0.2.14", ] [[package]] -name = "jsonrpsee-http-client" -version = "0.16.2" +name = "event-listener-strategy" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "async-trait", - "hyper", - "hyper-rustls", - "jsonrpsee-core", - "jsonrpsee-types", - "rustc-hash", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", + "event-listener 4.0.3", + "pin-project-lite 0.2.14", ] [[package]] -name = "jsonrpsee-proc-macros" -version = "0.16.2" +name = "event-listener-strategy" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "event-listener 5.3.0", + "pin-project-lite 0.2.14", ] [[package]] -name = "jsonrpsee-server" -version = "0.16.2" +name = "evm" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" +checksum = "767f43e9630cc36cf8ff2777cbb0121b055f0d1fd6eaaa13b46a1808f0d0e7e9" dependencies = [ - "futures-channel", - "futures-util", - "http", - "hyper", - "jsonrpsee-core", - "jsonrpsee-types", + "auto_impl", + "environmental", + "ethereum", + "evm-core", + "evm-gasometer", + "evm-runtime", + "log", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", "serde", - "serde_json", - "soketto", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tracing", + "sha3", ] [[package]] -name = "jsonrpsee-types" -version = "0.16.2" +name = "evm-core" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ - "anyhow", - "beef", + "parity-scale-codec", + "primitive-types", + "scale-info", "serde", - "serde_json", - "thiserror", - "tracing", ] [[package]] -name = "jsonrpsee-ws-client" -version = "0.16.2" +name = "evm-gasometer" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" +checksum = "1dc0eb591abc5cd7b05bef6a036c2bb6c66ab6c5e0c5ce94bfe377ab670b1fd7" dependencies = [ - "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", + "environmental", + "evm-core", + "evm-runtime", + "primitive-types", ] [[package]] -name = "k256" -version = "0.13.1" +name = "evm-runtime" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "84bbe09b64ae13a29514048c1bb6fda6374ac0b4f6a1f15a443348ab88ef42cd" dependencies = [ - "cfg-if", - "ecdsa 0.16.6", - "elliptic-curve 0.13.4", - "once_cell", - "sha2 0.10.8", + "auto_impl", + "environmental", + "evm-core", + "primitive-types", + "sha3", ] [[package]] -name = "keccak" -version = "0.1.3" +name = "exit-future" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "cpufeatures", + "futures 0.3.30", ] [[package]] -name = "kvdb" -version = "0.13.0" +name = "expander" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9" +checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" dependencies = [ - "smallvec", + "blake3", + "fs-err", + "proc-macro2", + "quote", ] [[package]] -name = "kvdb-memorydb" -version = "0.13.0" +name = "expander" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" +checksum = "00e83c02035136f1592a47964ea60c05a50e4ed8b5892cfac197063850898d4d" dependencies = [ - "kvdb", - "parking_lot 0.12.1", + "blake2 0.10.6", + "fs-err", + "prettier-please", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "kvdb-rocksdb" -version = "0.18.0" +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "faster-hex" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7a749456510c45f795e8b04a6a3e0976d0139213ecbf465843830ad55e2217" +checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ - "kvdb", - "num_cpus", - "parking_lot 0.12.1", - "regex", - "rocksdb", - "smallvec", + "instant", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "fastrand" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] -name = "lazycell" -version = "1.3.0" +name = "fatality" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "2ad875162843b0d046276327afe0136e9ed3a23d5a754210fb6f1f33610d39ab" +dependencies = [ + "fatality-proc-macro", + "thiserror", +] [[package]] -name = "libc" -version = "0.2.150" +name = "fatality-proc-macro" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" +dependencies = [ + "expander 0.0.4", + "indexmap 1.9.3", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror", +] [[package]] -name = "libloading" -version = "0.7.4" +name = "fc-api" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "async-trait", + "fp-storage", + "parity-scale-codec", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "fc-cli" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "clap", + "ethereum-types", + "fc-db", + "fp-rpc", + "fp-storage", + "sc-cli", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "fc-consensus" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "async-trait", + "fp-consensus", + "fp-rpc", + "sc-consensus", + "sp-api", + "sp-block-builder", + "sp-consensus", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "fc-db" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "async-trait", + "ethereum", + "fc-api", + "fc-storage", + "fp-consensus", + "fp-rpc", + "fp-storage", + "futures 0.3.30", + "kvdb-rocksdb", + "log", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.2", + "sc-client-api", + "sc-client-db", + "smallvec", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-runtime", + "sp-storage", + "sqlx", + "tokio", +] + +[[package]] +name = "fc-mapping-sync" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "fc-db", + "fc-storage", + "fp-consensus", + "fp-rpc", + "futures 0.3.30", + "futures-timer", + "log", + "parking_lot 0.12.2", + "sc-client-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "tokio", +] + +[[package]] +name = "fc-rpc" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "ethereum", + "ethereum-types", + "evm", + "fc-api", + "fc-mapping-sync", + "fc-rpc-core", + "fc-storage", + "fp-evm", + "fp-rpc", + "fp-storage", + "futures 0.3.30", + "hex", + "jsonrpsee", + "libsecp256k1", + "log", + "pallet-evm", + "parity-scale-codec", + "prometheus", + "rand", + "rlp", + "sc-client-api", + "sc-consensus-aura", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-rpc", + "sc-service", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sc-utils", + "schnellru", + "serde", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "sp-timestamp", + "substrate-prometheus-endpoint", + "thiserror", + "tokio", +] + +[[package]] +name = "fc-rpc-core" +version = "1.1.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "ethereum", + "ethereum-types", + "jsonrpsee", + "rustc-hex", + "serde", + "serde_json", +] + +[[package]] +name = "fc-storage" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-rpc", + "fp-storage", + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-io", + "sp-runtime", + "sp-storage", +] + +[[package]] +name = "fdlimit" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5" dependencies = [ - "cfg-if", - "winapi", + "libc", + "thiserror", ] [[package]] -name = "libm" -version = "0.2.6" +name = "ff" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.5.0", +] [[package]] -name = "libp2p" -version = "0.50.1" +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "file-per-thread-logger" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "bytes", - "futures", - "futures-timer", - "getrandom 0.2.11", - "instant", - "libp2p-core 0.38.0", - "libp2p-dns", - "libp2p-identify", - "libp2p-kad", - "libp2p-mdns", - "libp2p-metrics", - "libp2p-mplex", - "libp2p-noise", - "libp2p-ping", - "libp2p-quic", - "libp2p-request-response", - "libp2p-swarm", - "libp2p-tcp", - "libp2p-wasm-ext", - "libp2p-webrtc", - "libp2p-websocket", - "libp2p-yamux", - "multiaddr 0.16.0", - "parking_lot 0.12.1", - "pin-project", - "smallvec", + "env_logger", + "log", ] [[package]] -name = "libp2p-core" -version = "0.38.0" +name = "filetime" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "finality-grandpa" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" dependencies = [ - "asn1_der", - "bs58", - "ed25519-dalek", "either", - "fnv", - "futures", + "futures 0.3.30", "futures-timer", - "instant", "log", - "multiaddr 0.16.0", - "multihash 0.16.3", - "multistream-select", - "once_cell", - "parking_lot 0.12.1", - "pin-project", - "prost", - "prost-build", - "rand 0.8.5", - "rw-stream-sink", - "sec1 0.3.0", - "sha2 0.10.8", - "smallvec", - "thiserror", - "unsigned-varint", - "void", - "zeroize", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.2", + "scale-info", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flashbox-runtime" +version = "0.1.0" +dependencies = [ + "async-backing-primitives", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "cumulus-test-relay-sproof-builder", + "dp-consensus", + "dp-core", + "dp-slot-duration-runtime-api", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "nimbus-primitives", + "pallet-async-backing", + "pallet-author-inherent", + "pallet-author-noting", + "pallet-author-noting-runtime-api", + "pallet-authority-assignment", + "pallet-authority-mapping", + "pallet-balances", + "pallet-collator-assignment", + "pallet-collator-assignment-runtime-api", + "pallet-configuration", + "pallet-data-preservers", + "pallet-identity", + "pallet-inflation-rewards", + "pallet-initializer", + "pallet-invulnerables", + "pallet-maintenance-mode", + "pallet-migrations", + "pallet-multisig", + "pallet-proxy", + "pallet-registrar", + "pallet-registrar-runtime-api", + "pallet-relay-storage-roots", + "pallet-root-testing", + "pallet-services-payment", + "pallet-services-payment-runtime-api", + "pallet-session", + "pallet-stream-payment", + "pallet-stream-payment-runtime-api", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-tx-pause", + "pallet-utility", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "runtime-common", + "sc-consensus-grandpa", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-slots", + "sp-core", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-trie", + "sp-version", + "staging-parachain-info", + "substrate-wasm-builder", + "test-relay-sproof-builder", + "tp-author-noting-inherent", + "tp-traits", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin 0.9.8", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fork-tree" +version = "3.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "hex", + "impl-serde", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "fp-consensus" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "ethereum", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-ethereum" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm", + "frame-support", + "parity-scale-codec", + "sp-std", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "evm", + "frame-support", + "num_enum", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-rpc" +version = "3.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", +] + +[[package]] +name = "fp-self-contained" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", +] + +[[package]] +name = "fp-storage" +version = "2.0.0" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "parity-scale-codec", + "serde", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "frame-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-support-procedural", + "frame-system", + "linregress", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "static_assertions", +] + +[[package]] +name = "frame-benchmarking-cli" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "Inflector", + "array-bytes 6.2.3", + "chrono", + "clap", + "comfy-table", + "frame-benchmarking", + "frame-support", + "frame-system", + "gethostname", + "handlebars", + "itertools 0.10.5", + "lazy_static", + "linked-hash-map", + "log", + "parity-scale-codec", + "rand", + "rand_pcg", + "sc-block-builder", + "sc-cli", + "sc-client-api", + "sc-client-db", + "sc-executor", + "sc-service", + "sc-sysinfo", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "sp-trie", + "sp-wasm-interface", + "thiserror", + "thousands", +] + +[[package]] +name = "frame-election-provider-solution-type" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "frame-election-provider-support" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-election-provider-solution-type", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-executive" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-system", + "frame-try-runtime", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "frame-metadata" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-remote-externalities" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "futures 0.3.30", + "indicatif", + "jsonrpsee", + "log", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "spinners", + "substrate-rpc-client", + "tokio", + "tokio-retry", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "aquamarine", + "array-bytes 6.2.3", + "bitflags 1.3.2", + "docify", + "environmental", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "macro_magic", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "serde_json", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-metadata-ir", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-weights", + "static_assertions", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "Inflector", + "cfg-expr", + "derive-syn-parse 0.1.5", + "expander 2.1.0", + "frame-support-procedural-tools", + "itertools 0.10.5", + "macro_magic", + "proc-macro-warning", + "proc-macro2", + "quote", + "sp-core-hashing", + "syn 2.0.65", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "frame-support-test" +version = "3.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-executive", + "frame-metadata", + "frame-support", + "frame-support-test-pallet", + "frame-system", + "parity-scale-codec", + "pretty_assertions", + "rustversion", + "scale-info", + "serde", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-metadata-ir", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-version", + "static_assertions", + "trybuild", +] + +[[package]] +name = "frame-support-test-pallet" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "cfg-if", + "docify", + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "sp-weights", +] + +[[package]] +name = "frame-system-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-system-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + +[[package]] +name = "frame-try-runtime" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fs4" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" +dependencies = [ + "rustix 0.38.34", + "windows-sys 0.48.0", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.12.2", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.14", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.1.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite 0.2.14", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io", + "rustls 0.20.9", + "webpki", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures 0.1.31", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite 0.2.14", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom_or_panic" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" +dependencies = [ + "rand", + "rand_core 0.6.4", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug 0.3.1", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr 1.9.1", + "log", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle 2.5.0", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", + "serde", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.7", + "hmac 0.8.1", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite 0.2.14", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite 0.2.14", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls 0.21.12", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "webpki-roots 0.25.4", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if-addrs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "if-watch" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" +dependencies = [ + "async-io 2.3.2", + "core-foundation", + "fnv", + "futures 0.3.30", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "system-configuration", + "tokio", + "windows", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "impls" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a46645bbd70538861a90d0f26c31537cdf1e44aae99a794fb75a664b70951bc" + +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.7", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_executable" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +dependencies = [ + "winapi", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-ws-client", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots 0.25.4", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "anyhow", + "arrayvec 0.7.4", + "async-lock 2.8.0", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types", + "parking_lot 0.12.2", + "rand", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.3" +source = "git+https://github.com/moondance-labs/jsonrpsee?branch=tanssi-polkadot-v1.1.0#d6435421dba9d33886251d07c27c40403c954fa3" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keystream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" + +[[package]] +name = "kvdb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9" +dependencies = [ + "smallvec", +] + +[[package]] +name = "kvdb-memorydb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" +dependencies = [ + "kvdb", + "parking_lot 0.12.2", +] + +[[package]] +name = "kvdb-rocksdb" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b644c70b92285f66bfc2032922a79000ea30af7bc2ab31902992a5dcb9b434f6" +dependencies = [ + "kvdb", + "num_cpus", + "parking_lot 0.12.2", + "regex", + "rocksdb", + "smallvec", +] + +[[package]] +name = "landlock" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9baa9eeb6e315942429397e617a190f4fdc696ef1ee0342939d641029cbb4ea7" +dependencies = [ + "enumflags2", + "libc", + "thiserror", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libp2p" +version = "0.51.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f35eae38201a993ece6bdc823292d6abd1bffed1c4d0f4a3517d2bd8e1d917fe" +dependencies = [ + "bytes", + "futures 0.3.30", + "futures-timer", + "getrandom 0.2.15", + "instant", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core", + "libp2p-dns", + "libp2p-identify", + "libp2p-identity", + "libp2p-kad", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-noise", + "libp2p-ping", + "libp2p-quic", + "libp2p-request-response", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-wasm-ext", + "libp2p-websocket", + "libp2p-yamux", + "multiaddr", + "pin-project", +] + +[[package]] +name = "libp2p-allow-block-list" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510daa05efbc25184458db837f6f9a5143888f1caa742426d92e1833ddd38a50" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa33f1d26ed664c4fe2cca81a08c8e07d4c1c04f2f4ac7655c2dd85467fda0" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-core" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" +dependencies = [ + "either", + "fnv", + "futures 0.3.30", + "futures-timer", + "instant", + "libp2p-identity", + "log", + "multiaddr", + "multihash 0.17.0", + "multistream-select", + "once_cell", + "parking_lot 0.12.2", + "pin-project", + "quick-protobuf", + "rand", + "rw-stream-sink", + "smallvec", + "thiserror", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-dns" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146ff7034daae62077c415c2376b8057368042df6ab95f5432ad5e88568b1554" +dependencies = [ + "futures 0.3.30", + "libp2p-core", + "log", + "parking_lot 0.12.2", + "smallvec", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-identify" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5455f472243e63b9c497ff320ded0314254a9eb751799a39c283c6f20b793f3c" +dependencies = [ + "asynchronous-codec", + "either", + "futures 0.3.30", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "lru 0.10.1", + "quick-protobuf", + "quick-protobuf-codec", + "smallvec", + "thiserror", + "void", +] + +[[package]] +name = "libp2p-identity" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce" +dependencies = [ + "bs58 0.4.0", + "ed25519-dalek", + "log", + "multiaddr", + "multihash 0.17.0", + "quick-protobuf", + "rand", + "sha2 0.10.8", + "thiserror", + "zeroize", +] + +[[package]] +name = "libp2p-kad" +version = "0.43.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39d5ef876a2b2323d63c258e63c2f8e36f205fe5a11f0b3095d59635650790ff" +dependencies = [ + "arrayvec 0.7.4", + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures 0.3.30", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "quick-protobuf", + "rand", + "sha2 0.10.8", + "smallvec", + "thiserror", + "uint", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-mdns" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19983e1f949f979a928f2c603de1cf180cc0dc23e4ac93a62651ccb18341460b" +dependencies = [ + "data-encoding", + "futures 0.3.30", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "rand", + "smallvec", + "socket2 0.4.10", + "tokio", + "trust-dns-proto", + "void", +] + +[[package]] +name = "libp2p-metrics" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42ec91e227d7d0dafa4ce88b333cdf5f277253873ab087555c92798db2ddd46" +dependencies = [ + "libp2p-core", + "libp2p-identify", + "libp2p-kad", + "libp2p-ping", + "libp2p-swarm", + "prometheus-client", +] + +[[package]] +name = "libp2p-noise" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3673da89d29936bc6435bafc638e2f184180d554ce844db65915113f86ec5e" +dependencies = [ + "bytes", + "curve25519-dalek 3.2.0", + "futures 0.3.30", + "libp2p-core", + "libp2p-identity", + "log", + "once_cell", + "quick-protobuf", + "rand", + "sha2 0.10.8", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek 1.1.1", + "zeroize", +] + +[[package]] +name = "libp2p-ping" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e57759c19c28a73ef1eb3585ca410cefb72c1a709fcf6de1612a378e4219202" +dependencies = [ + "either", + "futures 0.3.30", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-swarm", + "log", + "rand", + "void", +] + +[[package]] +name = "libp2p-quic" +version = "0.7.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b26abd81cd2398382a1edfe739b539775be8a90fa6914f39b2ab49571ec735" +dependencies = [ + "bytes", + "futures 0.3.30", + "futures-timer", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-tls", + "log", + "parking_lot 0.12.2", + "quinn-proto", + "rand", + "rustls 0.20.9", + "thiserror", + "tokio", +] + +[[package]] +name = "libp2p-request-response" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffdb374267d42dc5ed5bc53f6e601d4a64ac5964779c6e40bb9e4f14c1e30d5" +dependencies = [ + "async-trait", + "futures 0.3.30", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "rand", + "smallvec", +] + +[[package]] +name = "libp2p-swarm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903b3d592d7694e56204d211f29d31bc004be99386644ba8731fc3e3ef27b296" +dependencies = [ + "either", + "fnv", + "futures 0.3.30", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm-derive", + "log", + "rand", + "smallvec", + "tokio", + "void", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f" +dependencies = [ + "heck 0.4.1", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "libp2p-tcp" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d33698596d7722d85d3ab0c86c2c322254fce1241e91208e3679b4eb3026cf" +dependencies = [ + "futures 0.3.30", + "futures-timer", + "if-watch", + "libc", + "libp2p-core", + "log", + "socket2 0.4.10", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" +dependencies = [ + "futures 0.3.30", + "futures-rustls", + "libp2p-core", + "libp2p-identity", + "rcgen", + "ring 0.16.20", + "rustls 0.20.9", + "thiserror", + "webpki", + "x509-parser", + "yasna", +] + +[[package]] +name = "libp2p-wasm-ext" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77dff9d32353a5887adb86c8afc1de1a94d9e8c3bc6df8b2201d7cdf5c848f43" +dependencies = [ + "futures 0.3.30", + "js-sys", + "libp2p-core", + "parity-send-wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "libp2p-websocket" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111273f7b3d3510524c752e8b7a5314b7f7a1fee7e68161c01a7d72cbb06db9f" +dependencies = [ + "either", + "futures 0.3.30", + "futures-rustls", + "libp2p-core", + "log", + "parking_lot 0.12.2", + "quicksink", + "rw-stream-sink", + "soketto", + "url", + "webpki-roots 0.22.6", +] + +[[package]] +name = "libp2p-yamux" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd21d950662700a385d4c6d68e2f5f54d778e97068cdd718522222ef513bda" +dependencies = [ + "futures 0.3.30", + "libp2p-core", + "log", + "thiserror", + "yamux", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "tikv-jemalloc-sys", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle 2.5.0", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "linregress" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" +dependencies = [ + "nalgebra", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lioness" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" +dependencies = [ + "arrayref", + "blake2 0.8.1", + "chacha", + "keystream", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lru" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lru" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "lru" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "lz4" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "macro_magic" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03844fc635e92f3a0067e25fa4bf3e3dbf3f2927bf3aa01bb7bc8f1c428949d" +dependencies = [ + "macro_magic_core", + "macro_magic_macros", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "macro_magic_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468155613a44cfd825f1fb0ffa532b018253920d404e6fca1e8d43155198a46d" +dependencies = [ + "const-random", + "derive-syn-parse 0.1.5", + "macro_magic_core_macros", + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "macro_magic_core_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea73aa640dc01d62a590d48c0c3521ed739d53b27f919b25c3551e233481654" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "macro_magic_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9d79ae96aaba821963320eb2b6e34d17df1e5a83d8a1985c29cc5be59577b3" +dependencies = [ + "macro_magic_core", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "manual-xcm-rpc" +version = "0.1.0" +dependencies = [ + "cumulus-primitives-core", + "flume 0.10.14", + "futures 0.3.30", + "hex-literal 0.3.4", + "jsonrpsee", + "parity-scale-codec", + "staging-xcm", + "tokio", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix 0.38.34", +] + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "mick-jaeger" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" +dependencies = [ + "futures 0.3.30", + "rand", + "thrift", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mixnet" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa3eb39495d8e2e2947a1d862852c90cc6a4a8845f8b41c8829cb9fcc047f4a" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "bitflags 1.3.2", + "blake2 0.10.6", + "c2-chacha", + "curve25519-dalek 4.1.2", + "either", + "hashlink", + "lioness", + "log", + "parking_lot 0.12.2", + "rand", + "rand_chacha 0.3.1", + "rand_distr", + "subtle 2.5.0", + "thiserror", + "zeroize", +] + +[[package]] +name = "mmr-gadget" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "futures 0.3.30", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-offchain", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-beefy", + "sp-core", + "sp-mmr-primitives", + "sp-runtime", +] + +[[package]] +name = "mmr-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "anyhow", + "jsonrpsee", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-mmr-primitives", + "sp-runtime", +] + +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "multiaddr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "log", + "multibase", + "multihash 0.17.0", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "blake2b_simd", + "blake2s_simd", + "blake3", + "core2", + "digest 0.10.7", + "multihash-derive 0.8.0", + "sha2 0.10.8", + "sha3", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd8a792c1694c6da4f68db0a9d707c72bd260994da179e6030a5dcee00bb815" +dependencies = [ + "core2", + "digest 0.10.7", + "multihash-derive 0.8.0", + "sha2 0.10.8", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +dependencies = [ + "core2", + "unsigned-varint", +] + +[[package]] +name = "multihash-codetable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d815ecb3c8238d00647f8630ede7060a642c9f704761cd6082cb4028af6935" +dependencies = [ + "blake2b_simd", + "blake2s_simd", + "blake3", + "core2", + "digest 0.10.7", + "multihash-derive 0.9.0", + "ripemd", + "serde", + "sha1", + "sha2 0.10.8", + "sha3", + "strobe-rs", +] + +[[package]] +name = "multihash-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multihash-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "890e72cb7396cb99ed98c1246a97b243cc16394470d94e0bc8b0c2c11d84290e" +dependencies = [ + "core2", + "multihash 0.19.1", + "multihash-derive-impl", +] + +[[package]] +name = "multihash-derive-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38685e08adb338659871ecfc6ee47ba9b22dcc8abcf6975d379cc49145c3040" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multistream-select" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" +dependencies = [ + "bytes", + "futures 0.3.30", + "log", + "pin-project", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "nalgebra" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea4908d4f23254adda3daa60ffef0f1ac7b8c3e9a864cf3cc154b251908a2ef" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "names" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" +dependencies = [ + "rand", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures 0.3.30", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" +dependencies = [ + "bytes", + "futures 0.3.30", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nimbus-consensus" +version = "0.9.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "async-backing-primitives", + "async-trait", + "cumulus-client-collator", + "cumulus-client-consensus-common", + "cumulus-client-consensus-proposer", + "cumulus-client-parachain-inherent", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "futures 0.3.30", + "log", + "nimbus-primitives", + "parity-scale-codec", + "parking_lot 0.12.2", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-primitives", + "sc-client-api", + "sc-consensus", + "sc-consensus-manual-seal", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-version", + "substrate-prometheus-endpoint", + "tracing", +] + +[[package]] +name = "nimbus-primitives" +version = "0.9.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "async-trait", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "no-std-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" + +[[package]] +name = "node-common" +version = "0.1.0" +dependencies = [ + "async-io 1.13.0", + "async-trait", + "clap", + "core_extensions", + "cumulus-client-cli", + "cumulus-client-collator", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-consensus-proposer", + "cumulus-client-network", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "flume 0.10.14", + "frame-benchmarking", + "frame-benchmarking-cli", + "futures 0.3.30", + "jsonrpsee", + "log", + "nimbus-consensus", + "nimbus-primitives", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives", + "polkadot-service", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-manual-seal", + "sc-consensus-slots", + "sc-executor", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-network-transactions", + "sc-offchain", + "sc-rpc", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sc-utils", + "serde", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-timestamp", + "sp-transaction-pool", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", + "tc-consensus", + "try-runtime-cli", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.4", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "orchestra" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92829eef0328a3d1cd22a02c0e51deb92a5362df3e7d21a4e9bdc38934694e66" +dependencies = [ + "async-trait", + "dyn-clonable", + "futures 0.3.30", + "futures-timer", + "orchestra-proc-macro", + "pin-project", + "prioritized-metered-channel 0.6.1", + "thiserror", + "tracing", +] + +[[package]] +name = "orchestra-proc-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1344346d5af32c95bbddea91b18a88cc83eac394192d20ef2fc4c40a74332355" +dependencies = [ + "expander 2.1.0", + "indexmap 2.2.6", + "itertools 0.11.0", + "petgraph", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + +[[package]] +name = "pallet-asset-conversion" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-asset-rate" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-asset-tx-payment" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-assets" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-async-backing" +version = "0.9.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-consensus-slots", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-author-inherent" +version = "0.9.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-author-noting" +version = "0.1.0" +dependencies = [ + "bounded-collections", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "dp-chain-state-snapshot", + "dp-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal 0.3.4", + "log", + "nimbus-primitives", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "scale-info", + "serde", + "sp-consensus-aura", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "test-relay-sproof-builder", + "tp-author-noting-inherent", + "tp-traits", +] + +[[package]] +name = "pallet-author-noting-runtime-api" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + +[[package]] +name = "pallet-authority-assignment" +version = "0.1.0" +dependencies = [ + "dp-collator-assignment", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-traits", +] + +[[package]] +name = "pallet-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-authority-discovery", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-authority-mapping" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-babe" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-babe", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-bags-list" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "aquamarine", + "docify", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-base-fee" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "pallet-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-consensus-beefy", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-beefy-mmr" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "array-bytes 6.2.3", + "binary-merkle-tree", + "frame-support", + "frame-system", + "log", + "pallet-beefy", + "pallet-mmr", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-consensus-beefy", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", +] + +[[package]] +name = "pallet-bounties" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bridge-grandpa" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bp-header-chain", + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-consensus-grandpa", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-messages" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "num-traits", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bridge-parachains" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bp-header-chain", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bridge-grandpa", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-relayers" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bp-messages", + "bp-relayers", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bridge-messages", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-broker" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-cc-authorities-noting" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" +dependencies = [ + "ccp-authorities-noting-inherent", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "dp-chain-state-snapshot", + "dp-collator-assignment", + "dp-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "serde", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-child-bounties" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bounties", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-collator-assignment" +version = "0.1.0" +dependencies = [ + "dp-collator-assignment", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "rand", + "rand_chacha 0.3.1", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-traits", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "pallet-collator-assignment-runtime-api" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", +] + +[[package]] +name = "pallet-collator-selection" +version = "3.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-collective" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-configuration" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-traits", +] + +[[package]] +name = "pallet-conviction-voting" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-data-preservers" +version = "0.1.0" +dependencies = [ + "bounded-collections", + "dp-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "similar-asserts", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-traits", +] + +[[package]] +name = "pallet-democracy" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-department-funding" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-support-test", + "frame-system", + "pallet-balances", + "pallet-schelling-game-shared", + "pallet-shared-storage", + "pallet-sortition-sum-game", + "pallet-support", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "trait-schelling-game-shared", + "trait-shared-storage", +] + +[[package]] +name = "pallet-election-provider-multi-phase" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-election-provider-support-benchmarking", + "parity-scale-codec", + "rand", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std", + "strum 0.24.1", +] + +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-elections-phragmen" +version = "5.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-ethereum" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "ethereum", + "ethereum-types", + "evm", + "fp-consensus", + "fp-ethereum", + "fp-evm", + "fp-rpc", + "fp-storage", + "frame-support", + "frame-system", + "pallet-evm", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "environmental", + "evm", + "fp-account", + "fp-evm", + "frame-benchmarking", + "frame-support", + "frame-system", + "hash-db", + "hex", + "hex-literal 0.4.1", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-chain-id" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "pallet-evm-precompile-balances-erc20" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum", + "pallet-balances", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "paste", + "precompile-utils", + "slices", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-batch" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum", + "pallet-evm", + "parity-scale-codec", + "paste", + "precompile-utils", + "slices", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-call-permit" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "paste", + "precompile-utils", + "slices", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-modexp" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "fp-evm", + "num", +] + +[[package]] +name = "pallet-evm-precompile-sha3fips" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "fp-evm", + "tiny-keccak", +] + +[[package]] +name = "pallet-evm-precompile-simple" +version = "2.0.0-dev" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" +dependencies = [ + "fp-evm", + "ripemd", + "sp-io", +] + +[[package]] +name = "pallet-evm-precompile-xcm-utils" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "num_enum", + "pallet-evm", + "pallet-xcm", + "parity-scale-codec", + "precompile-utils", + "sp-core", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", + "staging-xcm-executor", + "xcm-primitives", +] + +[[package]] +name = "pallet-evm-precompileset-assets-erc20" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "pallet-assets", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "paste", + "precompile-utils", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-fast-unstake" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-foreign-asset-creator" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", +] + +[[package]] +name = "pallet-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-grandpa", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-identity" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-im-online" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-indices" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-inflation-rewards" +version = "0.1.0" +dependencies = [ + "bounded-collections", + "dp-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "similar-asserts", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-traits", +] + +[[package]] +name = "pallet-initializer" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-insecure-randomness-collective-flip" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "safe-mix", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-invulnerables" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-session", + "parity-scale-codec", + "rand", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", + "tp-traits", +] + +[[package]] +name = "pallet-maintenance-mode" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", + "xcm-primitives", +] + +[[package]] +name = "pallet-membership" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-message-queue" +version = "7.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", +] + +[[package]] +name = "pallet-migrations" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm-primitives", +] + +[[package]] +name = "pallet-mmr" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-multisig" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-core" -version = "0.39.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" +name = "pallet-nis" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "libp2p-identity", - "log", - "multiaddr 0.17.1", - "multihash 0.17.0", - "multistream-select", - "once_cell", - "parking_lot 0.12.1", - "pin-project", - "quick-protobuf", - "rand 0.8.5", - "rw-stream-sink", - "smallvec", - "thiserror", - "unsigned-varint", - "void", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-dns" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" +name = "pallet-nomination-pools" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", - "libp2p-core 0.38.0", + "frame-support", + "frame-system", "log", - "parking_lot 0.12.1", - "smallvec", - "trust-dns-resolver", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-tracing", ] [[package]] -name = "libp2p-identify" -version = "0.41.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" +name = "pallet-nomination-pools-benchmarking" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "asynchronous-codec", - "futures", - "futures-timer", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "lru", - "prost", - "prost-build", - "prost-codec", - "smallvec", - "thiserror", - "void", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "pallet-bags-list", + "pallet-nomination-pools", + "pallet-staking", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-runtime-interface", + "sp-staking", + "sp-std", ] [[package]] -name = "libp2p-identity" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" +name = "pallet-nomination-pools-runtime-api" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "bs58", - "ed25519-dalek", - "log", - "multiaddr 0.17.1", - "multihash 0.17.0", - "quick-protobuf", - "rand 0.8.5", - "sha2 0.10.8", - "thiserror", - "zeroize", + "pallet-nomination-pools", + "parity-scale-codec", + "sp-api", + "sp-std", ] [[package]] -name = "libp2p-kad" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" +name = "pallet-offences" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "arrayvec 0.7.4", - "asynchronous-codec", - "bytes", - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm", + "frame-support", + "frame-system", "log", - "prost", - "prost-build", - "rand 0.8.5", - "sha2 0.10.8", - "smallvec", - "thiserror", - "uint", - "unsigned-varint", - "void", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] -name = "libp2p-mdns" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" +name = "pallet-offences-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "data-encoding", - "futures", - "if-watch", - "libp2p-core 0.38.0", - "libp2p-swarm", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", "log", - "rand 0.8.5", - "smallvec", - "socket2", - "tokio", - "trust-dns-proto", - "void", + "pallet-babe", + "pallet-balances", + "pallet-grandpa", + "pallet-im-online", + "pallet-offences", + "pallet-session", + "pallet-staking", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] -name = "libp2p-metrics" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" +name = "pallet-pooled-staking" +version = "0.1.0" dependencies = [ - "libp2p-core 0.38.0", - "libp2p-identify", - "libp2p-kad", - "libp2p-ping", - "libp2p-swarm", - "prometheus-client", + "dp-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "similar-asserts", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-maths", + "tp-traits", ] [[package]] -name = "libp2p-mplex" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" +name = "pallet-positive-externality" +version = "4.0.0-dev" dependencies = [ - "asynchronous-codec", - "bytes", - "futures", - "libp2p-core 0.38.0", - "log", - "nohash-hasher", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec", - "unsigned-varint", + "frame-benchmarking", + "frame-support", + "frame-support-test", + "frame-system", + "pallet-balances", + "pallet-schelling-game-shared", + "pallet-shared-storage", + "pallet-sortition-sum-game", + "pallet-support", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "trait-schelling-game-shared", + "trait-shared-storage", ] [[package]] -name = "libp2p-noise" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" +name = "pallet-preimage" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "bytes", - "curve25519-dalek 3.2.0", - "futures", - "libp2p-core 0.38.0", + "frame-benchmarking", + "frame-support", + "frame-system", "log", - "once_cell", - "prost", - "prost-build", - "rand 0.8.5", - "sha2 0.10.8", - "snow", - "static_assertions", - "thiserror", - "x25519-dalek 1.1.1", - "zeroize", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-ping" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" +name = "pallet-profile-validation" +version = "4.0.0-dev" dependencies = [ - "futures", - "futures-timer", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "rand 0.8.5", - "void", + "frame-benchmarking", + "frame-support", + "frame-support-test", + "frame-system", + "pallet-balances", + "pallet-schelling-game-shared", + "pallet-sortition-sum-game", + "pallet-support", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "trait-schelling-game-shared", ] [[package]] -name = "libp2p-quic" -version = "0.7.0-alpha" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" +name = "pallet-project-tips" +version = "4.0.0-dev" dependencies = [ - "bytes", - "futures", - "futures-timer", - "if-watch", - "libp2p-core 0.38.0", - "libp2p-tls", - "log", - "parking_lot 0.12.1", - "quinn-proto", - "rand 0.8.5", - "rustls 0.20.8", - "thiserror", - "tokio", + "frame-benchmarking", + "frame-support", + "frame-support-test", + "frame-system", + "pallet-balances", + "pallet-schelling-game-shared", + "pallet-shared-storage", + "pallet-sortition-sum-game", + "pallet-support", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "trait-schelling-game-shared", + "trait-shared-storage", ] [[package]] -name = "libp2p-request-response" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" +name = "pallet-proxy" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "async-trait", - "bytes", - "futures", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "rand 0.8.5", - "smallvec", - "unsigned-varint", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-swarm" -version = "0.41.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" +name = "pallet-ranked-collective" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm-derive", + "frame-benchmarking", + "frame-support", + "frame-system", "log", - "pin-project", - "rand 0.8.5", - "smallvec", - "thiserror", - "tokio", - "void", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-swarm-derive" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" +name = "pallet-recovery" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "heck", - "quote", - "syn 1.0.109", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-tcp" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" +name = "pallet-referenda" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", - "futures-timer", - "if-watch", - "libc", - "libp2p-core 0.38.0", + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", "log", - "socket2", - "tokio", + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-tls" +name = "pallet-registrar" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ - "futures", - "futures-rustls", - "libp2p-core 0.39.2", - "libp2p-identity", - "rcgen 0.10.0", - "ring 0.16.20", - "rustls 0.20.8", - "thiserror", - "webpki 0.22.0", - "x509-parser 0.14.0", - "yasna", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-configuration", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-container-chain-genesis-data", + "tp-traits", ] [[package]] -name = "libp2p-wasm-ext" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" +name = "pallet-registrar-runtime-api" +version = "0.1.0" dependencies = [ - "futures", - "js-sys", - "libp2p-core 0.38.0", - "parity-send-wrapper", - "wasm-bindgen", - "wasm-bindgen-futures", + "frame-support", + "pallet-registrar", + "parity-scale-codec", + "scale-info", + "sp-api", + "tp-container-chain-genesis-data", ] [[package]] -name = "libp2p-webrtc" -version = "0.4.0-alpha" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" +name = "pallet-relay-storage-roots" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" dependencies = [ - "async-trait", - "asynchronous-codec", - "bytes", - "futures", - "futures-timer", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", "hex", - "if-watch", - "libp2p-core 0.38.0", - "libp2p-noise", "log", - "multihash 0.16.3", - "prost", - "prost-build", - "prost-codec", - "rand 0.8.5", - "rcgen 0.9.3", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", "serde", - "stun", - "thiserror", - "tinytemplate", - "tokio", - "tokio-util", - "webrtc", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-websocket" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" +name = "pallet-root-testing" +version = "1.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "either", - "futures", - "futures-rustls", - "libp2p-core 0.38.0", - "log", - "parking_lot 0.12.1", - "quicksink", - "rw-stream-sink", - "soketto", - "url", - "webpki-roots", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "libp2p-yamux" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" +name = "pallet-scheduler" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", - "libp2p-core 0.38.0", + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", "log", - "parking_lot 0.12.1", - "thiserror", - "yamux", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", ] [[package]] -name = "librocksdb-sys" -version = "0.10.0+7.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b" +name = "pallet-schelling-game-shared" +version = "4.0.0-dev" dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", - "libc", - "libz-sys", - "tikv-jemalloc-sys", + "frame-benchmarking", + "frame-support", + "frame-support-test", + "frame-system", + "num-integer", + "pallet-balances", + "pallet-sortition-sum-game", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "trait-schelling-game-shared", + "trait-sortition-sum-game", ] [[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +name = "pallet-services-payment" +version = "0.1.0" dependencies = [ - "arrayref", - "base64 0.13.1", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", "serde", - "sha2 0.9.9", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tp-traits", ] [[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +name = "pallet-services-payment-runtime-api" +version = "0.1.0" dependencies = [ - "libsecp256k1-core", + "parity-scale-codec", + "sp-api", ] [[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +name = "pallet-session" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "libsecp256k1-core", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-trie", ] [[package]] -name = "libz-sys" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +name = "pallet-session-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "cc", - "pkg-config", - "vcpkg", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-session", + "pallet-staking", + "parity-scale-codec", + "rand", + "sp-runtime", + "sp-session", + "sp-std", ] [[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +name = "pallet-shared-storage" +version = "4.0.0-dev" dependencies = [ - "cc", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "trait-shared-storage", ] [[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linked_hash_set" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +name = "pallet-society" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "linked-hash-map", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "linregress" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" +name = "pallet-sortition-sum-game" +version = "4.0.0-dev" dependencies = [ - "nalgebra", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "trait-sortition-sum-game", ] [[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "linux-raw-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +name = "pallet-spaces" +version = "4.0.0-dev" dependencies = [ - "autocfg", - "scopeguard", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-support", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +name = "pallet-staking" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "cfg-if", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "serde", + "sp-application-crypto", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] -name = "lru" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +name = "pallet-staking-reward-curve" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "hashbrown 0.12.3", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +name = "pallet-staking-reward-fn" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "linked-hash-map", + "log", + "sp-arithmetic", ] [[package]] -name = "lz4" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +name = "pallet-staking-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "libc", - "lz4-sys", + "parity-scale-codec", + "sp-api", + "sp-staking", ] [[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +name = "pallet-state-trie-migration" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "cc", - "libc", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +name = "pallet-stream-payment" +version = "0.1.0" dependencies = [ - "libc", + "dp-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "similar-asserts", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "tap", + "tp-maths", + "tp-traits", ] [[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "match_cfg" +name = "pallet-stream-payment-runtime-api" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "regex-automata 0.1.10", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "thiserror", ] [[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matrixmultiply" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" +name = "pallet-sudo" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "autocfg", - "rawpointer", + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "md-5" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +name = "pallet-support" +version = "0.1.0" dependencies = [ - "digest 0.10.7", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "strum 0.26.2", ] [[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" - -[[package]] -name = "memfd" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +name = "pallet-template" +version = "4.0.0-dev" dependencies = [ - "rustix 0.37.19", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", ] [[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +name = "pallet-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "libc", + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-storage", + "sp-timestamp", ] [[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +name = "pallet-tips" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "autocfg", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +name = "pallet-transaction-payment" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "autocfg", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "memory-db" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +name = "pallet-transaction-payment-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "hash-db", + "jsonrpsee", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-weights", ] [[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - -[[package]] -name = "merlin" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +name = "pallet-transaction-payment-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "byteorder", - "keccak", - "rand_core 0.5.1", - "zeroize", + "pallet-transaction-payment", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-weights", ] [[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +name = "pallet-treasury" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] [[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +name = "pallet-tx-pause" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "adler", + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-proxy", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", ] [[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +name = "pallet-utility" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "adler", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +name = "pallet-vesting" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "libc", + "frame-benchmarking", + "frame-support", + "frame-system", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", ] [[package]] -name = "mockall" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +name = "pallet-whitelist" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std", ] [[package]] -name = "mockall_derive" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +name = "pallet-xcm" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 1.0.109", + "bounded-collections", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] -name = "multiaddr" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" +name = "pallet-xcm-benchmarks" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "arrayref", - "byteorder", - "data-encoding", - "multibase", - "multihash 0.16.3", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] -name = "multiaddr" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +name = "pallet-xcm-bridge-hub-router" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "arrayref", - "byteorder", - "data-encoding", + "bp-xcm-bridge-hub-router", + "frame-benchmarking", + "frame-support", + "frame-system", "log", - "multibase", - "multihash 0.17.0", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", ] [[package]] -name = "multibase" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +name = "pallet-xcm-core-buyer" +version = "0.1.0" dependencies = [ - "base-x", - "data-encoding", - "data-encoding-macro", + "bounded-collections", + "dp-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "num-traits", + "pallet-balances", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "serde", + "similar-asserts", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "tp-traits", ] [[package]] -name = "multihash" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +name = "pallet-xcm-executor-utils" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "blake2b_simd", - "blake2s_simd", - "blake3", - "core2", - "digest 0.10.7", - "multihash-derive", - "sha2 0.10.8", - "sha3", - "unsigned-varint", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", ] [[package]] -name = "multihash" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +name = "parachains-common" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "core2", - "multihash-derive", - "unsigned-varint", + "cumulus-primitives-core", + "cumulus-primitives-utility", + "frame-support", + "frame-system", + "log", + "num-traits", + "pallet-asset-tx-payment", + "pallet-assets", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-message-queue", + "pallet-xcm", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-primitives", + "rococo-runtime-constants", + "scale-info", + "smallvec", + "sp-consensus-aura", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", + "westend-runtime-constants", ] [[package]] -name = "multihash-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" -dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", +name = "parachains-runtimes-test-utils" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "assets-common", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "pallet-assets", + "pallet-balances", + "pallet-collator-selection", + "pallet-session", + "pallet-xcm", + "parachains-common", + "parity-scale-codec", + "polkadot-parachain-primitives", + "sp-consensus-aura", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-executor", + "substrate-wasm-builder", ] [[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "multistream-select" -version = "0.12.1" +name = "parity-db" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" +checksum = "592a28a24b09c9dc20ac8afaa6839abc417c720afe42c12e1e4a9d6aa2508d2e" dependencies = [ - "bytes", - "futures", + "blake2 0.10.6", + "crc32fast", + "fs2", + "hex", + "libc", "log", - "pin-project", - "smallvec", - "unsigned-varint", + "lz4", + "memmap2", + "parking_lot 0.12.2", + "rand", + "siphasher", + "snap", + "winapi", ] [[package]] -name = "nalgebra" -version = "0.32.2" +name = "parity-scale-codec" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros", - "num-complex", - "num-rational", - "num-traits", - "simba", - "typenum", + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", ] [[package]] -name = "nalgebra-macros" -version = "0.2.0" +name = "parity-scale-codec-derive" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "names" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d66043b25d4a6cccb23619d10c19c25304b355a7dccd4a8e11423dd2382146" -dependencies = [ - "rand 0.8.5", -] - -[[package]] -name = "netlink-packet-core" -version = "0.4.2" +name = "parity-send-wrapper" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" -dependencies = [ - "anyhow", - "byteorder", - "libc", - "netlink-packet-utils", -] +checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" [[package]] -name = "netlink-packet-route" +name = "parity-util-mem" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" dependencies = [ - "anyhow", - "bitflags", - "byteorder", - "libc", - "netlink-packet-core", - "netlink-packet-utils", + "cfg-if", + "ethereum-types", + "hashbrown 0.12.3", + "impl-trait-for-tuples", + "lru 0.8.1", + "parity-util-mem-derive", + "parking_lot 0.12.2", + "primitive-types", + "smallvec", + "winapi", ] [[package]] -name = "netlink-packet-utils" -version = "0.5.2" +name = "parity-util-mem-derive" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror", + "proc-macro2", + "syn 1.0.109", + "synstructure", ] [[package]] -name = "netlink-proto" -version = "0.10.0" +name = "parity-wasm" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" -dependencies = [ - "bytes", - "futures", - "log", - "netlink-packet-core", - "netlink-sys", - "thiserror", - "tokio", -] +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] -name = "netlink-sys" -version = "0.8.5" +name = "parking" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" -dependencies = [ - "bytes", - "futures", - "libc", - "log", - "tokio", -] +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] -name = "nix" -version = "0.24.3" +name = "parking_lot" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", + "instant", + "lock_api", + "parking_lot_core 0.8.6", ] [[package]] -name = "node-template" -version = "4.0.0-dev" +name = "parking_lot" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ - "clap", - "department-funding-rpc", - "department-funding-runtime-api", - "frame-benchmarking", - "frame-benchmarking-cli", - "frame-system", - "futures", - "hex-literal", - "jsonrpsee", - "node-template-runtime", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc", - "positive-externality-rpc", - "positive-externality-runtime-api", - "profile-validation-rpc", - "profile-validation-runtime-api", - "project-tips-rpc", - "project-tips-runtime-api", - "sc-basic-authorship", - "sc-cli", - "sc-client-api", - "sc-consensus", - "sc-consensus-aura", - "sc-consensus-grandpa", - "sc-executor", - "sc-keystore", - "sc-rpc", - "sc-rpc-api", - "sc-service", - "sc-telemetry", - "sc-transaction-pool", - "sc-transaction-pool-api", - "sp-api", - "sp-block-builder", - "sp-blockchain", - "sp-consensus", - "sp-consensus-aura", - "sp-consensus-grandpa", - "sp-core", - "sp-inherents", - "sp-io", - "sp-keyring", - "sp-runtime", - "sp-timestamp", - "substrate-build-script-utils", - "substrate-frame-rpc-system", - "try-runtime-cli", + "lock_api", + "parking_lot_core 0.9.10", ] [[package]] -name = "node-template-runtime" -version = "4.0.0-dev" +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "department-funding", - "department-funding-runtime-api", - "frame-benchmarking", - "frame-executive", - "frame-support", - "frame-system", - "frame-system-benchmarking", - "frame-system-rpc-runtime-api", - "frame-try-runtime", - "pallet-aura", - "pallet-balances", - "pallet-grandpa", - "pallet-insecure-randomness-collective-flip", - "pallet-sudo", - "pallet-template", - "pallet-timestamp", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc-runtime-api", - "parity-scale-codec", - "positive-externality", - "positive-externality-runtime-api", - "profile-validation", - "profile-validation-runtime-api", - "project-tips", - "project-tips-runtime-api", - "scale-info", - "schelling-game-shared", - "shared-storage", - "sortition-sum-game", - "sp-api", - "sp-block-builder", - "sp-consensus-aura", - "sp-consensus-grandpa", - "sp-core", - "sp-inherents", - "sp-offchain", - "sp-runtime", - "sp-session", - "sp-std", - "sp-transaction-pool", - "sp-version", - "substrate-wasm-builder", + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - -[[package]] -name = "nom" -version = "7.1.3" +name = "parking_lot_core" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "memchr", - "minimal-lexical", + "cfg-if", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", ] [[package]] -name = "normalize-line-endings" -version = "0.3.0" +name = "partial_sort" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +checksum = "7924d1d0ad836f665c9065e26d016c673ece3993f30d340068b16f282afc1156" [[package]] -name = "num-bigint" -version = "0.4.3" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "num-complex" -version = "0.4.3" +name = "pbkdf2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" dependencies = [ - "num-traits", + "crypto-mac 0.11.0", ] [[package]] -name = "num-format" -version = "0.4.4" +name = "pbkdf2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "arrayvec 0.7.4", - "itoa", + "digest 0.10.7", ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "peeking_take_while" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] -name = "num-rational" -version = "0.4.1" +name = "pem" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", + "base64 0.13.1", ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "num_cpus" -version = "1.15.0" +name = "pest" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ - "hermit-abi 0.2.6", - "libc", + "memchr", + "thiserror", + "ucd-trie", ] [[package]] -name = "number_prefix" -version = "0.4.0" +name = "pest_derive" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" +dependencies = [ + "pest", + "pest_generator", +] [[package]] -name = "object" -version = "0.29.0" +name = "pest_generator" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ - "crc32fast", - "hashbrown 0.12.3", - "indexmap", - "memchr", + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] -name = "object" -version = "0.30.3" +name = "pest_meta" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ - "memchr", + "once_cell", + "pest", + "sha2 0.10.8", ] [[package]] -name = "oid-registry" -version = "0.4.0" +name = "petgraph" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "asn1-rs 0.3.1", + "fixedbitset", + "indexmap 2.2.6", ] [[package]] -name = "oid-registry" -version = "0.6.1" +name = "pin-project" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ - "asn1-rs 0.5.2", + "pin-project-internal", ] [[package]] -name = "once_cell" -version = "1.18.0" +name = "pin-project-internal" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] [[package]] -name = "opaque-debug" -version = "0.2.3" +name = "pin-project-lite" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "pin-project-lite" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] -name = "openssl-probe" -version = "0.1.5" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "output_vt100" -version = "0.1.3" +name = "piper" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" dependencies = [ - "winapi", + "atomic-waker", + "fastrand 2.1.0", + "futures-io", ] [[package]] -name = "p256" -version = "0.11.1" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.8", + "der", + "spki", ] [[package]] -name = "p384" -version = "0.11.2" +name = "pkg-config" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" -dependencies = [ - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.8", -] - -[[package]] -name = "pallet-aura" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-application-crypto", - "sp-consensus-aura", - "sp-runtime", - "sp-std", -] +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] -name = "pallet-authorship" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "impl-trait-for-tuples", - "parity-scale-codec", - "scale-info", - "sp-runtime", - "sp-std", -] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] -name = "pallet-balances" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-approval-distribution" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-runtime", - "sp-std", + "bitvec", + "futures 0.3.30", + "futures-timer", + "itertools 0.10.5", + "polkadot-node-jaeger", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand", + "tracing-gum", ] [[package]] -name = "pallet-election" -version = "4.0.0-dev" +name = "polkadot-availability-bitfield-distribution" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-support-test", - "frame-system", - "log", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-npos-elections", - "sp-runtime", + "always-assert", + "futures 0.3.30", + "futures-timer", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand", + "tracing-gum", ] [[package]] -name = "pallet-grandpa" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-availability-distribution" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "pallet-authorship", - "pallet-session", + "derive_more", + "fatality", + "futures 0.3.30", "parity-scale-codec", - "scale-info", - "sp-application-crypto", - "sp-consensus-grandpa", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand", + "schnellru", "sp-core", - "sp-io", - "sp-runtime", - "sp-session", - "sp-staking", - "sp-std", -] - -[[package]] -name = "pallet-insecure-randomness-collective-flip" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "safe-mix", - "scale-info", - "sp-runtime", - "sp-std", + "sp-keystore", + "thiserror", + "tracing-gum", ] [[package]] -name = "pallet-posts" -version = "4.0.0-dev" +name = "polkadot-availability-recovery" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-spaces", - "pallet-support", - "pallet-timestamp", + "async-trait", + "fatality", + "futures 0.3.30", "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand", + "sc-network", + "schnellru", + "thiserror", + "tokio", + "tracing-gum", ] [[package]] -name = "pallet-session" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-cli" +version = "1.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-support", - "frame-system", - "impl-trait-for-tuples", + "cfg-if", + "clap", + "frame-benchmarking-cli", + "futures 0.3.30", "log", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", + "polkadot-node-metrics", + "polkadot-node-primitives", + "polkadot-service", + "sc-cli", + "sc-executor", + "sc-service", + "sc-storage-monitor", + "sc-sysinfo", + "sc-tracing", "sp-core", "sp-io", - "sp-runtime", - "sp-session", - "sp-staking", - "sp-std", - "sp-trie", + "sp-keyring", + "sp-maybe-compressed-blob", + "substrate-build-script-utils", + "thiserror", + "try-runtime-cli", ] [[package]] -name = "pallet-spaces" -version = "4.0.0-dev" +name = "polkadot-collator-protocol" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-support", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", + "bitvec", + "fatality", + "futures 0.3.30", + "futures-timer", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", "sp-core", - "sp-io", + "sp-keystore", "sp-runtime", + "thiserror", + "tokio-util", + "tracing-gum", ] [[package]] -name = "pallet-sudo" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-core-primitives" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-support", - "frame-system", "parity-scale-codec", "scale-info", - "sp-io", + "sp-core", "sp-runtime", "sp-std", ] [[package]] -name = "pallet-support" -version = "0.1.0" +name = "polkadot-dispute-distribution" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-support-test", - "frame-system", - "log", - "pallet-balances", - "pallet-timestamp", + "derive_more", + "fatality", + "futures 0.3.30", + "futures-timer", + "indexmap 1.9.3", "parity-scale-codec", - "scale-info", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "strum", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sc-network", + "schnellru", + "sp-application-crypto", + "sp-keystore", + "thiserror", + "tracing-gum", ] [[package]] -name = "pallet-tags" -version = "4.0.0-dev" +name = "polkadot-erasure-coding" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", "parity-scale-codec", - "scale-info", + "polkadot-node-primitives", + "polkadot-primitives", + "reed-solomon-novelpoly", "sp-core", - "sp-io", - "sp-runtime", + "sp-trie", + "thiserror", ] [[package]] -name = "pallet-template" -version = "4.0.0-dev" +name = "polkadot-gossip-support" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", + "futures 0.3.30", + "futures-timer", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand", + "rand_chacha 0.3.1", + "sc-network", + "sc-network-common", + "sp-application-crypto", "sp-core", - "sp-io", - "sp-runtime", + "sp-keystore", + "tracing-gum", ] [[package]] -name = "pallet-timestamp" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-network-bridge" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", + "always-assert", + "async-trait", + "bytes", + "fatality", + "futures 0.3.30", "parity-scale-codec", - "scale-info", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-std", - "sp-timestamp", + "parking_lot 0.12.2", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sc-network", + "sp-consensus", + "thiserror", + "tracing-gum", ] [[package]] -name = "pallet-transaction-payment" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-node-collation-generation" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-support", - "frame-system", + "futures 0.3.30", "parity-scale-codec", - "scale-info", - "serde", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-maybe-compressed-blob", + "thiserror", + "tracing-gum", ] [[package]] -name = "pallet-transaction-payment-rpc" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-node-core-approval-voting" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "jsonrpsee", - "pallet-transaction-payment-rpc-runtime-api", + "bitvec", + "derive_more", + "futures 0.3.30", + "futures-timer", + "itertools 0.10.5", + "kvdb", + "merlin", "parity-scale-codec", - "sp-api", - "sp-blockchain", - "sp-core", - "sp-rpc", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "rand", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "sc-keystore", + "schnellru", + "schnorrkel 0.11.4", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", "sp-runtime", - "sp-weights", + "thiserror", + "tracing-gum", ] [[package]] -name = "pallet-transaction-payment-rpc-runtime-api" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "polkadot-node-core-av-store" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "pallet-transaction-payment", + "bitvec", + "futures 0.3.30", + "futures-timer", + "kvdb", "parity-scale-codec", - "sp-api", - "sp-runtime", - "sp-weights", + "polkadot-erasure-coding", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sp-consensus", + "thiserror", + "tracing-gum", ] [[package]] -name = "pallet-ubi" -version = "4.0.0-dev" +name = "polkadot-node-core-backing" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-support-test", - "frame-system", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "shared-storage", - "shared-storage-link", - "sp-core", - "sp-io", - "sp-runtime", + "bitvec", + "fatality", + "futures 0.3.30", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "polkadot-statement-table", + "sp-keystore", + "thiserror", + "tracing-gum", ] [[package]] -name = "parity-db" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd4572a52711e2ccff02b4973ec7e4a5b5c23387ebbfbd6cd42b34755714cefc" +name = "polkadot-node-core-bitfield-signing" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "blake2", - "crc32fast", - "fs2", - "hex", - "libc", - "log", - "lz4", - "memmap2", - "parking_lot 0.12.1", - "rand 0.8.5", - "siphasher", - "snap", + "futures 0.3.30", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-keystore", + "thiserror", + "tracing-gum", + "wasm-timer", ] [[package]] -name = "parity-scale-codec" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" +name = "polkadot-node-core-candidate-validation" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "arrayvec 0.7.4", - "bitvec", - "byte-slice-cast", - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", + "async-trait", + "futures 0.3.30", + "futures-timer", + "parity-scale-codec", + "polkadot-node-core-pvf", + "polkadot-node-metrics", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-parachain-primitives", + "polkadot-primitives", + "sp-maybe-compressed-blob", + "tracing-gum", ] [[package]] -name = "parity-scale-codec-derive" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +name = "polkadot-node-core-chain-api" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "futures 0.3.30", + "polkadot-node-metrics", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "sc-client-api", + "sc-consensus-babe", + "tracing-gum", ] [[package]] -name = "parity-send-wrapper" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" +name = "polkadot-node-core-chain-selection" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "futures 0.3.30", + "futures-timer", + "kvdb", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "thiserror", + "tracing-gum", +] [[package]] -name = "parity-wasm" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" +name = "polkadot-node-core-dispute-coordinator" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "fatality", + "futures 0.3.30", + "kvdb", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sc-keystore", + "schnellru", + "thiserror", + "tracing-gum", +] [[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" +name = "polkadot-node-core-parachains-inherent" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "futures 0.3.30", + "futures-timer", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sp-blockchain", + "sp-inherents", + "thiserror", + "tracing-gum", +] [[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +name = "polkadot-node-core-prospective-parachains" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", + "bitvec", + "fatality", + "futures 0.3.30", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "thiserror", + "tracing-gum", ] [[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +name = "polkadot-node-core-provisioner" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "lock_api", - "parking_lot_core 0.9.7", + "bitvec", + "fatality", + "futures 0.3.30", + "futures-timer", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "thiserror", + "tracing-gum", ] [[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +name = "polkadot-node-core-pvf" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "always-assert", + "array-bytes 6.2.3", + "blake3", "cfg-if", - "instant", + "futures 0.3.30", + "futures-timer", + "is_executable", "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parity-scale-codec", + "pin-project", + "polkadot-core-primitives", + "polkadot-node-core-pvf-common", + "polkadot-node-metrics", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-parachain-primitives", + "polkadot-primitives", + "rand", + "slotmap", + "sp-core", + "sp-maybe-compressed-blob", + "sp-wasm-interface", + "tempfile", + "thiserror", + "tokio", + "tracing-gum", ] [[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +name = "polkadot-node-core-pvf-checker" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "futures 0.3.30", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-pvf-common" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "cfg-if", + "cpu-time", + "futures 0.3.30", + "landlock", "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "sc-executor", + "sc-executor-common", + "sc-executor-wasmtime", + "seccompiler", + "sp-core", + "sp-externalities", + "sp-io", + "sp-tracing", + "thiserror", + "tracing-gum", ] [[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "pbkdf2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +name = "polkadot-node-core-runtime-api" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "crypto-mac 0.11.1", + "futures 0.3.30", + "polkadot-node-metrics", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "polkadot-primitives", + "schnellru", + "sp-consensus-babe", + "tracing-gum", ] [[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +name = "polkadot-node-jaeger" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "digest 0.10.7", + "lazy_static", + "log", + "mick-jaeger", + "parity-scale-codec", + "parking_lot 0.12.2", + "polkadot-node-primitives", + "polkadot-primitives", + "sc-network", + "sp-core", + "thiserror", + "tokio", ] [[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +name = "polkadot-node-metrics" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "bs58 0.5.1", + "futures 0.3.30", + "futures-timer", + "log", + "parity-scale-codec", + "polkadot-primitives", + "prioritized-metered-channel 0.5.1", + "sc-cli", + "sc-service", + "sc-tracing", + "substrate-prometheus-endpoint", + "tracing-gum", +] [[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +name = "polkadot-node-network-protocol" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "base64 0.13.1", + "async-channel 1.9.0", + "async-trait", + "bitvec", + "derive_more", + "fatality", + "futures 0.3.30", + "hex", + "parity-scale-codec", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-primitives", + "rand", + "sc-authority-discovery", + "sc-network", + "strum 0.24.1", + "thiserror", + "tracing-gum", ] [[package]] -name = "pem-rfc7468" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +name = "polkadot-node-primitives" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "base64ct", + "bitvec", + "bounded-vec", + "futures 0.3.30", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "schnorrkel 0.11.4", + "serde", + "sp-application-crypto", + "sp-consensus-babe", + "sp-core", + "sp-keystore", + "sp-maybe-compressed-blob", + "sp-runtime", + "thiserror", + "zstd 0.12.4", ] [[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +name = "polkadot-node-subsystem" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "polkadot-node-jaeger", + "polkadot-node-subsystem-types", + "polkadot-overseer", +] [[package]] -name = "pest" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +name = "polkadot-node-subsystem-types" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "async-trait", + "bitvec", + "derive_more", + "futures 0.3.30", + "orchestra", + "polkadot-node-jaeger", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-primitives", + "polkadot-statement-table", + "sc-client-api", + "sc-network", + "sc-transaction-pool-api", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-consensus-babe", + "sp-runtime", + "substrate-prometheus-endpoint", "thiserror", - "ucd-trie", ] [[package]] -name = "pest_derive" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +name = "polkadot-node-subsystem-util" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "pest", - "pest_generator", + "async-trait", + "derive_more", + "fatality", + "futures 0.3.30", + "futures-channel", + "itertools 0.10.5", + "kvdb", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.2", + "pin-project", + "polkadot-node-jaeger", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "polkadot-overseer", + "polkadot-primitives", + "prioritized-metered-channel 0.5.1", + "rand", + "sc-client-api", + "schnellru", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror", + "tracing-gum", ] [[package]] -name = "pest_generator" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +name = "polkadot-overseer" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.39", + "async-trait", + "futures 0.3.30", + "futures-timer", + "orchestra", + "parking_lot 0.12.2", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem-types", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-core", + "tikv-jemalloc-ctl", + "tracing-gum", ] [[package]] -name = "pest_meta" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +name = "polkadot-parachain-primitives" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "once_cell", - "pest", - "sha2 0.10.8", + "bounded-collections", + "derive_more", + "parity-scale-codec", + "polkadot-core-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", + "sp-weights", ] [[package]] -name = "petgraph" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +name = "polkadot-primitives" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "fixedbitset", - "indexmap", + "bitvec", + "hex-literal 0.4.1", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +name = "polkadot-rpc" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "pin-project-internal", + "jsonrpsee", + "mmr-rpc", + "pallet-transaction-payment-rpc", + "polkadot-primitives", + "sc-chain-spec", + "sc-client-api", + "sc-consensus-babe", + "sc-consensus-babe-rpc", + "sc-consensus-beefy", + "sc-consensus-beefy-rpc", + "sc-consensus-epochs", + "sc-consensus-grandpa", + "sc-consensus-grandpa-rpc", + "sc-rpc", + "sc-rpc-spec-v2", + "sc-sync-state-rpc", + "sc-transaction-pool-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-keystore", + "sp-runtime", + "substrate-frame-rpc-system", + "substrate-state-trie-migration-rpc", ] [[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +name = "polkadot-runtime-common" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "libsecp256k1", + "log", + "pallet-asset-rate", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-broker", + "pallet-election-provider-multi-phase", + "pallet-fast-unstake", + "pallet-identity", + "pallet-session", + "pallet-staking", + "pallet-staking-reward-fn", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-treasury", + "pallet-vesting", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "slot-range-helper", + "sp-api", + "sp-core", + "sp-inherents", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "static_assertions", ] [[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +name = "polkadot-runtime-metrics" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "der 0.6.1", - "spki 0.6.0", + "bs58 0.5.1", + "frame-benchmarking", + "parity-scale-codec", + "polkadot-primitives", + "sp-std", + "sp-tracing", ] [[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +name = "polkadot-runtime-parachains" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "der 0.7.5", - "spki 0.7.1", + "bitflags 1.3.2", + "bitvec", + "derive_more", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-broker", + "pallet-message-queue", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "pallet-vesting", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-metrics", + "rand", + "rand_chacha 0.3.1", + "rustc-hex", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "staging-xcm", + "staging-xcm-executor", + "static_assertions", ] [[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +name = "polkadot-service" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "frame-benchmarking", + "frame-benchmarking-cli", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "futures 0.3.30", + "hex-literal 0.4.1", + "is_executable", + "kvdb", + "kvdb-rocksdb", + "log", + "mmr-gadget", + "pallet-babe", + "pallet-im-online", + "pallet-staking", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.2", + "polkadot-approval-distribution", + "polkadot-availability-bitfield-distribution", + "polkadot-availability-distribution", + "polkadot-availability-recovery", + "polkadot-collator-protocol", + "polkadot-core-primitives", + "polkadot-dispute-distribution", + "polkadot-gossip-support", + "polkadot-network-bridge", + "polkadot-node-collation-generation", + "polkadot-node-core-approval-voting", + "polkadot-node-core-av-store", + "polkadot-node-core-backing", + "polkadot-node-core-bitfield-signing", + "polkadot-node-core-candidate-validation", + "polkadot-node-core-chain-api", + "polkadot-node-core-chain-selection", + "polkadot-node-core-dispute-coordinator", + "polkadot-node-core-parachains-inherent", + "polkadot-node-core-prospective-parachains", + "polkadot-node-core-provisioner", + "polkadot-node-core-pvf", + "polkadot-node-core-pvf-checker", + "polkadot-node-core-runtime-api", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-rpc", + "polkadot-runtime-parachains", + "polkadot-statement-distribution", + "rococo-runtime", + "sc-authority-discovery", + "sc-basic-authorship", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-consensus-babe", + "sc-consensus-beefy", + "sc-consensus-grandpa", + "sc-consensus-slots", + "sc-executor", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-offchain", + "sc-service", + "sc-sync-state-rpc", + "sc-sysinfo", + "sc-telemetry", + "sc-transaction-pool", + "sc-transaction-pool-api", + "schnellru", + "serde", + "serde_json", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-keystore", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-storage", + "sp-timestamp", + "sp-transaction-pool", + "sp-version", + "sp-weights", + "substrate-prometheus-endpoint", + "thiserror", + "tracing-gum", + "westend-runtime", +] [[package]] -name = "platforms" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94" +name = "polkadot-statement-distribution" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "fatality", + "futures 0.3.30", + "futures-timer", + "indexmap 1.9.3", + "parity-scale-codec", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-keystore", + "sp-staking", + "thiserror", + "tracing-gum", +] [[package]] -name = "platforms" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" +name = "polkadot-statement-table" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "parity-scale-codec", + "polkadot-primitives", + "sp-core", +] [[package]] name = "polling" @@ -5403,106 +11283,120 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", "log", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.14", "windows-sys 0.48.0", ] [[package]] -name = "poly1305" -version = "0.8.0" +name = "polling" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.5.0", + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite 0.2.14", + "rustix 0.38.34", + "tracing", + "windows-sys 0.52.0", ] [[package]] -name = "polyval" -version = "0.5.3" +name = "poly1305" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ - "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "opaque-debug 0.3.1", + "universal-hash", ] [[package]] name = "polyval" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.5.0", + "opaque-debug 0.3.1", + "universal-hash", ] [[package]] name = "portable-atomic" -version = "1.3.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] -name = "positive-externality" -version = "4.0.0-dev" +name = "positive-externality-runtime-api" +version = "0.1.0" dependencies = [ - "frame-benchmarking", "frame-support", - "frame-support-test", - "frame-system", - "pallet-balances", - "pallet-support", - "pallet-timestamp", "parity-scale-codec", - "scale-info", - "schelling-game-shared", - "schelling-game-shared-link", - "shared-storage", - "shared-storage-link", - "sortition-sum-game", - "sp-core", - "sp-io", - "sp-runtime", + "sp-api", + "sp-std", ] [[package]] -name = "positive-externality-rpc" +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precompile-utils" version = "0.1.0" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" dependencies = [ - "jsonrpsee", - "positive-externality-runtime-api", - "sc-rpc", - "sc-rpc-api", - "sp-api", - "sp-blockchain", + "environmental", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log", + "num_enum", + "pallet-evm", + "parity-scale-codec", + "precompile-utils-macro", + "sp-core", + "sp-io", "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", ] [[package]] -name = "positive-externality-runtime-api" +name = "precompile-utils-macro" version = "0.1.0" +source = "git+https://github.com/moondance-labs/frontier?branch=tanssi-polkadot-v1.6.0#4414529b910e8cf802969b11505a14665e4a55d1" dependencies = [ - "frame-support", - "sp-api", + "case", + "num_enum", + "prettyplease 0.2.20", + "proc-macro2", + "quote", + "sp-core-hashing", + "syn 1.0.109", ] -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "predicates" version = "2.1.5" @@ -5511,7 +11405,7 @@ checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", - "itertools", + "itertools 0.10.5", "normalize-line-endings", "predicates-core", "regex", @@ -5533,49 +11427,109 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettier-please" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22020dfcf177fcc7bf5deaf7440af371400c67c0de14c399938d8ed4fb4645d3" +dependencies = [ + "proc-macro2", + "syn 2.0.65", +] + [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] [[package]] name = "prettyplease" -version = "0.1.25" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "f28f53e8b192565862cf99343194579a022eb9c7dd3a8d03134734803c7b3125" dependencies = [ "proc-macro2", "syn 1.0.109", ] [[package]] -name = "primitive-types" -version = "0.12.1" +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.65", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "prioritized-metered-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e99f0c89bd88f393aab44a4ab949351f7bc7e7e1179d11ecbfe50cbe4c47e342" +dependencies = [ + "coarsetime", + "crossbeam-queue", + "derive_more", + "futures 0.3.30", + "futures-timer", + "nanorand", + "thiserror", + "tracing", +] + +[[package]] +name = "prioritized-metered-channel" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a172e6cc603231f2cf004232eabcecccc0da53ba576ab286ef7baa0cfc7927ad" +dependencies = [ + "coarsetime", + "crossbeam-queue", + "derive_more", + "futures 0.3.30", + "futures-timer", + "nanorand", + "thiserror", + "tracing", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ - "fixed-hash", - "impl-codec", - "impl-serde", - "scale-info", - "uint", + "once_cell", + "toml_edit 0.19.15", ] [[package]] name = "proc-macro-crate" -version = "1.1.3" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "thiserror", - "toml 0.5.11", + "toml_edit 0.21.1", ] [[package]] @@ -5604,100 +11558,32 @@ dependencies = [ [[package]] name = "proc-macro-warning" -version = "0.3.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" +checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] -[[package]] -name = "profile-validation" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-support-test", - "frame-system", - "pallet-balances", - "pallet-support", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "schelling-game-shared", - "schelling-game-shared-link", - "sortition-sum-game", - "sp-core", - "sp-io", - "sp-runtime", -] - -[[package]] -name = "profile-validation-rpc" -version = "0.1.0" -dependencies = [ - "jsonrpsee", - "profile-validation-runtime-api", - "sc-rpc", - "sc-rpc-api", - "sp-api", - "sp-blockchain", - "sp-runtime", -] - [[package]] name = "profile-validation-runtime-api" version = "0.1.0" dependencies = [ "frame-support", - "sp-api", -] - -[[package]] -name = "project-tips" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-support-test", - "frame-system", - "pallet-balances", - "pallet-support", - "pallet-timestamp", "parity-scale-codec", - "scale-info", - "schelling-game-shared", - "schelling-game-shared-link", - "shared-storage", - "shared-storage-link", - "sortition-sum-game", - "sp-core", - "sp-io", - "sp-runtime", -] - -[[package]] -name = "project-tips-rpc" -version = "0.1.0" -dependencies = [ - "jsonrpsee", - "project-tips-runtime-api", - "sc-rpc", - "sc-rpc-api", "sp-api", - "sp-blockchain", - "sp-runtime", + "sp-std", ] [[package]] @@ -5705,44 +11591,46 @@ name = "project-tips-runtime-api" version = "0.1.0" dependencies = [ "frame-support", + "parity-scale-codec", "sp-api", + "sp-std", ] [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "thiserror", ] [[package]] name = "prometheus-client" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +checksum = "5d6fa99d535dd930d1249e6c79cb3c2915f9172a540fe2b02a4c8f9ca954721e" dependencies = [ "dtoa", "itoa", - "parking_lot 0.12.1", - "prometheus-client-derive-text-encode", + "parking_lot 0.12.2", + "prometheus-client-derive-encode", ] [[package]] -name = "prometheus-client-derive-text-encode" -version = "0.3.0" +name = "prometheus-client-derive-encode" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.65", ] [[package]] @@ -5762,13 +11650,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", - "heck", - "itertools", + "heck 0.4.1", + "itertools 0.10.5", "lazy_static", "log", "multimap", "petgraph", - "prettyplease", + "prettyplease 0.1.11", "prost", "prost-types", "regex", @@ -5777,19 +11665,6 @@ dependencies = [ "which", ] -[[package]] -name = "prost-codec" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" -dependencies = [ - "asynchronous-codec", - "bytes", - "prost", - "thiserror", - "unsigned-varint", -] - [[package]] name = "prost-derive" version = "0.11.9" @@ -5797,7 +11672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -5836,6 +11711,19 @@ dependencies = [ "byteorder", ] +[[package]] +name = "quick-protobuf-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1693116345026436eb2f10b677806169c1a1260c1c60eaaffe3fb5a29ae23d8b" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror", + "unsigned-varint", +] + [[package]] name = "quicksink" version = "0.1.2" @@ -5849,27 +11737,27 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" dependencies = [ "bytes", - "rand 0.8.5", + "rand", "ring 0.16.20", "rustc-hash", - "rustls 0.20.8", + "rustls 0.20.9", "slab", "thiserror", "tinyvec", "tracing", - "webpki 0.22.0", + "webpki", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -5880,19 +11768,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -5939,16 +11814,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "rand_distr" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ - "rand_core 0.5.1", + "num-traits", + "rand", ] [[package]] @@ -5968,9 +11844,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -5978,9 +11854,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -5988,83 +11864,92 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring 0.16.20", "time", - "x509-parser 0.13.2", "yasna", ] [[package]] -name = "rcgen" -version = "0.10.0" +name = "redox_syscall" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "pem", - "ring 0.16.20", - "time", - "yasna", + "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags", + "bitflags 2.5.0", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.11", - "redox_syscall 0.2.16", + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "reed-solomon-novelpoly" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58130877ca403ab42c864fbac74bb319a0746c07a634a92a5cfc7f54af272582" +dependencies = [ + "derive_more", + "fs-err", + "itertools 0.11.0", + "static_init", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "regalloc2" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621" dependencies = [ "fxhash", "log", @@ -6074,14 +11959,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ - "aho-corasick 1.0.1", + "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -6095,13 +11980,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ - "aho-corasick 1.0.1", + "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -6112,21 +11997,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "region" -version = "3.0.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags", - "libc", - "mach", - "winapi", -] +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "resolv-conf" @@ -6138,17 +12011,6 @@ dependencies = [ "quick-error", ] -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac 0.12.1", - "zeroize", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -6156,7 +12018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac 0.12.1", - "subtle", + "subtle 2.5.0", ] [[package]] @@ -6176,48 +12038,181 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.11", + "cfg-if", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "rocksdb" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rococo-runtime" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "binary-merkle-tree", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.4.1", + "log", + "pallet-asset-rate", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-conviction-voting", + "pallet-democracy", + "pallet-elections-phragmen", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-message-queue", + "pallet-mmr", + "pallet-multisig", + "pallet-nis", + "pallet-offences", + "pallet-preimage", + "pallet-proxy", + "pallet-ranked-collective", + "pallet-recovery", + "pallet-referenda", + "pallet-root-testing", + "pallet-scheduler", + "pallet-session", + "pallet-society", + "pallet-staking", + "pallet-state-trie-migration", + "pallet-sudo", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rococo-runtime-constants", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-storage", + "sp-transaction-pool", + "sp-version", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "static_assertions", + "substrate-wasm-builder", +] + +[[package]] +name = "rococo-runtime-constants" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "libc", - "librocksdb-sys", + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", + "staging-xcm", + "staging-xcm-builder", ] [[package]] name = "rpassword" -version = "7.2.0" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" dependencies = [ "libc", "rtoolbox", - "winapi", -] - -[[package]] -name = "rtcp" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1919efd6d4a6a85d13388f9487549bb8e359f17198cc03ffd72f79b553873691" -dependencies = [ - "bytes", - "thiserror", - "webrtc-util", + "windows-sys 0.48.0", ] [[package]] @@ -6226,7 +12221,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" dependencies = [ - "futures", + "futures 0.3.30", "log", "netlink-packet-route", "netlink-proto", @@ -6237,33 +12232,45 @@ dependencies = [ [[package]] name = "rtoolbox" -version = "0.0.1" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] -name = "rtp" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +name = "runtime-common" +version = "0.1.0" dependencies = [ - "async-trait", - "bytes", - "rand 0.8.5", - "serde", - "thiserror", - "webrtc-util", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "frame-try-runtime", + "hex-literal 0.3.4", + "pallet-balances", + "pallet-configuration", + "pallet-data-preservers", + "pallet-invulnerables", + "pallet-migrations", + "pallet-pooled-staking", + "pallet-registrar", + "pallet-services-payment", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -6292,7 +12299,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", + "semver 1.0.23", ] [[package]] @@ -6306,11 +12313,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.13" +version = "0.36.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -6320,48 +12327,60 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys 0.3.7", + "linux-raw-sys 0.3.8", "windows-sys 0.48.0", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" -version = "0.19.1" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ - "base64 0.13.1", "log", "ring 0.16.20", - "sct 0.6.1", - "webpki 0.21.4", + "sct", + "webpki", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.16.20", - "sct 0.7.0", - "webpki 0.22.0", + "ring 0.17.8", + "rustls-webpki", + "sct", ] [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -6371,18 +12390,39 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "base64 0.21.0", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ruzstd" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc" +dependencies = [ + "byteorder", + "thiserror-core", + "twox-hash", +] [[package]] name = "rw-stream-sink" @@ -6390,16 +12430,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" dependencies = [ - "futures", + "futures 0.3.30", "pin-project", "static_assertions", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "safe-mix" @@ -6412,9 +12452,9 @@ dependencies = [ [[package]] name = "safe_arch" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" dependencies = [ "bytemuck", ] @@ -6431,7 +12471,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "log", "sp-core", @@ -6439,17 +12479,45 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-authority-discovery" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "futures 0.3.30", + "futures-timer", + "ip_network", + "libp2p", + "log", + "multihash 0.18.1", + "multihash-codetable", + "parity-scale-codec", + "prost", + "prost-build", + "rand", + "sc-client-api", + "sc-network", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", + "futures 0.3.30", "futures-timer", "log", "parity-scale-codec", "sc-block-builder", - "sc-client-api", "sc-proposer-metrics", "sc-telemetry", "sc-transaction-pool-api", @@ -6465,24 +12533,28 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", - "sc-client-api", "sp-api", "sp-block-builder", "sp-blockchain", "sp-core", "sp-inherents", "sp-runtime", + "sp-trie", ] [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "array-bytes 6.2.3", + "docify", + "log", "memmap2", + "parity-scale-codec", "sc-chain-spec-derive", "sc-client-api", "sc-executor", @@ -6492,6 +12564,8 @@ dependencies = [ "serde_json", "sp-blockchain", "sp-core", + "sp-genesis-builder", + "sp-io", "sp-runtime", "sp-state-machine", ] @@ -6499,36 +12573,39 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", + "bip39", "chrono", "clap", "fdlimit", - "futures", - "libp2p", + "futures 0.3.30", + "itertools 0.10.5", + "libp2p-identity", "log", "names", "parity-scale-codec", - "rand 0.8.5", + "rand", "regex", "rpassword", "sc-client-api", "sc-client-db", + "sc-executor", "sc-keystore", + "sc-mixnet", "sc-network", - "sc-network-common", "sc-service", "sc-telemetry", "sc-tracing", @@ -6541,22 +12618,23 @@ dependencies = [ "sp-keystore", "sp-panic-handler", "sp-runtime", + "sp-state-machine", + "sp-storage", "sp-version", "thiserror", - "tiny-bip39", "tokio", ] [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "fnv", - "futures", + "futures 0.3.30", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sc-executor", "sc-transaction-pool-api", "sc-utils", @@ -6566,17 +12644,18 @@ dependencies = [ "sp-core", "sp-database", "sp-externalities", - "sp-keystore", "sp-runtime", "sp-state-machine", + "sp-statement-store", "sp-storage", + "sp-trie", "substrate-prometheus-endpoint", ] [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "hash-db", "kvdb", @@ -6586,109 +12665,290 @@ dependencies = [ "log", "parity-db", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sc-client-api", "sc-state-db", "schnellru", "sp-arithmetic", "sp-blockchain", - "sp-core", - "sp-database", + "sp-core", + "sp-database", + "sp-runtime", + "sp-state-machine", + "sp-trie", +] + +[[package]] +name = "sc-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "futures 0.3.30", + "futures-timer", + "libp2p-identity", + "log", + "mockall", + "parking_lot 0.12.2", + "sc-client-api", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "futures 0.3.30", + "log", + "parity-scale-codec", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-consensus-slots", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-babe" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "fork-tree", + "futures 0.3.30", + "log", + "num-bigint", + "num-rational", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.2", + "sc-client-api", + "sc-consensus", + "sc-consensus-epochs", + "sc-consensus-slots", + "sc-telemetry", + "sc-transaction-pool-api", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-babe-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "futures 0.3.30", + "jsonrpsee", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-rpc-api", + "serde", + "sp-api", + "sp-application-crypto", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-keystore", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-consensus-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "array-bytes 6.2.3", + "async-channel 1.9.0", + "async-trait", + "fnv", + "futures 0.3.30", + "log", + "parity-scale-codec", + "parking_lot 0.12.2", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-gossip", + "sc-network-sync", + "sc-utils", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-beefy", + "sp-core", + "sp-keystore", + "sp-mmr-primitives", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "tokio", + "wasm-timer", +] + +[[package]] +name = "sc-consensus-beefy-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "futures 0.3.30", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.2", + "sc-consensus-beefy", + "sc-rpc", + "serde", + "sp-consensus-beefy", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-consensus-epochs" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "fork-tree", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sp-blockchain", "sp-runtime", - "sp-state-machine", - "sp-trie", ] [[package]] -name = "sc-consensus" +name = "sc-consensus-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "ahash 0.8.11", + "array-bytes 6.2.3", "async-trait", - "futures", + "dyn-clone", + "finality-grandpa", + "fork-tree", + "futures 0.3.30", "futures-timer", - "libp2p", "log", - "mockall", - "parking_lot 0.12.1", + "parity-scale-codec", + "parking_lot 0.12.2", + "rand", + "sc-block-builder", + "sc-chain-spec", "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-network-sync", + "sc-telemetry", + "sc-transaction-pool-api", "sc-utils", - "serde", + "serde_json", "sp-api", + "sp-application-crypto", + "sp-arithmetic", "sp-blockchain", "sp-consensus", + "sp-consensus-grandpa", "sp-core", + "sp-keystore", "sp-runtime", - "sp-state-machine", "substrate-prometheus-endpoint", "thiserror", ] [[package]] -name = "sc-consensus-aura" +name = "sc-consensus-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "async-trait", - "futures", + "finality-grandpa", + "futures 0.3.30", + "jsonrpsee", "log", "parity-scale-codec", - "sc-block-builder", "sc-client-api", - "sc-consensus", - "sc-consensus-slots", - "sc-telemetry", - "sp-api", - "sp-application-crypto", - "sp-block-builder", + "sc-consensus-grandpa", + "sc-rpc", + "serde", "sp-blockchain", - "sp-consensus", - "sp-consensus-aura", - "sp-consensus-slots", "sp-core", - "sp-inherents", - "sp-keystore", "sp-runtime", - "substrate-prometheus-endpoint", "thiserror", ] [[package]] -name = "sc-consensus-grandpa" +name = "sc-consensus-manual-seal" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "ahash 0.8.7", - "array-bytes", + "assert_matches", "async-trait", - "dyn-clone", - "finality-grandpa", - "fork-tree", - "futures", + "futures 0.3.30", "futures-timer", + "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "sc-block-builder", - "sc-chain-spec", "sc-client-api", "sc-consensus", - "sc-network", - "sc-network-common", - "sc-network-gossip", - "sc-telemetry", - "sc-utils", - "serde_json", + "sc-consensus-aura", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", "sp-api", - "sp-application-crypto", - "sp-arithmetic", "sp-blockchain", "sp-consensus", - "sp-consensus-grandpa", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-slots", "sp-core", + "sp-inherents", "sp-keystore", "sp-runtime", + "sp-timestamp", "substrate-prometheus-endpoint", "thiserror", ] @@ -6696,10 +12956,10 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", - "futures", + "futures 0.3.30", "futures-timer", "log", "parity-scale-codec", @@ -6719,14 +12979,14 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "lru", + "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sc-executor-common", - "sc-executor-wasmi", "sc-executor-wasmtime", + "schnellru", "sp-api", "sp-core", "sp-externalities", @@ -6737,48 +12997,36 @@ dependencies = [ "sp-version", "sp-wasm-interface", "tracing", - "wasmi", ] [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "parity-scale-codec", "sc-allocator", "sp-maybe-compressed-blob", "sp-wasm-interface", "thiserror", "wasm-instrument", - "wasmi", -] - -[[package]] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "log", - "sc-allocator", - "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", - "wasmi", ] [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "anyhow", "cfg-if", "libc", "log", - "once_cell", - "rustix 0.36.13", + "parity-scale-codec", + "parking_lot 0.12.2", + "rustix 0.36.17", "sc-allocator", "sc-executor-common", + "sp-core", "sp-runtime-interface", "sp-wasm-interface", "wasmtime", @@ -6787,15 +13035,16 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "ansi_term", - "futures", + "futures 0.3.30", "futures-timer", "log", "sc-client-api", "sc-network", "sc-network-common", + "sc-network-sync", "sp-blockchain", "sp-runtime", ] @@ -6803,11 +13052,10 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", - "async-trait", - "parking_lot 0.12.1", + "array-bytes 6.2.3", + "parking_lot 0.12.2", "serde_json", "sp-application-crypto", "sp-core", @@ -6815,65 +13063,92 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-mixnet" +version = "0.1.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "array-bytes 4.2.0", + "arrayvec 0.7.4", + "blake2 0.10.6", + "bytes", + "futures 0.3.30", + "futures-timer", + "libp2p-identity", + "log", + "mixnet", + "multiaddr", + "parity-scale-codec", + "parking_lot 0.12.2", + "sc-client-api", + "sc-network", + "sc-transaction-pool-api", + "sp-api", + "sp-consensus", + "sp-core", + "sp-keystore", + "sp-mixnet", + "sp-runtime", + "thiserror", +] + [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", - "async-channel", + "array-bytes 6.2.3", + "async-channel 1.9.0", "async-trait", "asynchronous-codec", "bytes", "either", "fnv", - "futures", + "futures 0.3.30", "futures-timer", "ip_network", "libp2p", "linked_hash_set", "log", - "lru", "mockall", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", + "partial_sort", "pin-project", - "rand 0.8.5", - "sc-block-builder", + "rand", "sc-client-api", - "sc-consensus", "sc-network-common", - "sc-peerset", "sc-utils", "serde", "serde_json", "smallvec", - "snow", "sp-arithmetic", "sp-blockchain", - "sp-consensus", "sp-core", "sp-runtime", "substrate-prometheus-endpoint", "thiserror", + "tokio", + "tokio-stream", "unsigned-varint", + "wasm-timer", "zeroize", ] [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "async-channel 1.9.0", "cid", - "futures", - "libp2p", + "futures 0.3.30", + "libp2p-identity", "log", "prost", "prost-build", "sc-client-api", "sc-network", - "sc-network-common", "sp-blockchain", "sp-runtime", "thiserror", @@ -6883,45 +13158,34 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", "async-trait", - "bitflags", - "bytes", - "futures", - "futures-timer", - "libp2p", + "bitflags 1.3.2", + "futures 0.3.30", + "libp2p-identity", "parity-scale-codec", "prost-build", "sc-consensus", - "sc-peerset", - "sc-utils", - "serde", - "smallvec", - "sp-blockchain", "sp-consensus", "sp-consensus-grandpa", "sp-runtime", - "substrate-prometheus-endpoint", - "thiserror", - "zeroize", ] [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "ahash 0.8.7", - "futures", + "ahash 0.8.11", + "futures 0.3.30", "futures-timer", "libp2p", "log", - "lru", "sc-network", "sc-network-common", - "sc-peerset", + "sc-network-sync", + "schnellru", "sp-runtime", "substrate-prometheus-endpoint", "tracing", @@ -6930,19 +13194,18 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", - "futures", - "libp2p", + "array-bytes 6.2.3", + "async-channel 1.9.0", + "futures 0.3.30", + "libp2p-identity", "log", "parity-scale-codec", "prost", "prost-build", "sc-client-api", "sc-network", - "sc-network-common", - "sc-peerset", "sp-blockchain", "sp-core", "sp-runtime", @@ -6952,16 +13215,16 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", + "async-channel 1.9.0", "async-trait", "fork-tree", - "futures", + "futures 0.3.30", "futures-timer", "libp2p", "log", - "lru", "mockall", "parity-scale-codec", "prost", @@ -6970,8 +13233,8 @@ dependencies = [ "sc-consensus", "sc-network", "sc-network-common", - "sc-peerset", "sc-utils", + "schnellru", "smallvec", "sp-arithmetic", "sp-blockchain", @@ -6981,22 +13244,54 @@ dependencies = [ "sp-runtime", "substrate-prometheus-endpoint", "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "sc-network-test" +version = "0.8.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "async-trait", + "futures 0.3.30", + "futures-timer", + "libp2p", + "log", + "parking_lot 0.12.2", + "rand", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-network-light", + "sc-network-sync", + "sc-service", + "sc-utils", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-tracing", + "substrate-test-runtime", + "substrate-test-runtime-client", + "tokio", ] [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", - "futures", + "array-bytes 6.2.3", + "futures 0.3.30", "libp2p", "log", "parity-scale-codec", - "pin-project", "sc-network", "sc-network-common", - "sc-peerset", + "sc-network-sync", "sc-utils", "sp-consensus", "sp-runtime", @@ -7006,51 +13301,41 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", "bytes", "fnv", - "futures", + "futures 0.3.30", "futures-timer", "hyper", "hyper-rustls", "libp2p", + "log", "num_cpus", "once_cell", "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", + "parking_lot 0.12.2", + "rand", "sc-client-api", "sc-network", "sc-network-common", - "sc-peerset", + "sc-transaction-pool-api", "sc-utils", "sp-api", "sp-core", + "sp-externalities", + "sp-keystore", "sp-offchain", "sp-runtime", "threadpool", "tracing", ] -[[package]] -name = "sc-peerset" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "futures", - "libp2p", - "log", - "sc-utils", - "serde_json", - "wasm-timer", -] - [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -7059,16 +13344,17 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", + "futures 0.3.30", "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sc-block-builder", "sc-chain-spec", "sc-client-api", + "sc-mixnet", "sc-rpc-api", "sc-tracing", "sc-transaction-pool-api", @@ -7082,6 +13368,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-session", + "sp-statement-store", "sp-version", "tokio", ] @@ -7089,11 +13376,12 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "jsonrpsee", "parity-scale-codec", "sc-chain-spec", + "sc-mixnet", "sc-transaction-pool-api", "scale-info", "serde", @@ -7108,7 +13396,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "http", "jsonrpsee", @@ -7123,46 +13411,48 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", - "futures", + "array-bytes 6.2.3", + "futures 0.3.30", "futures-util", "hex", "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sc-chain-spec", "sc-client-api", "sc-transaction-pool-api", + "sc-utils", "serde", "sp-api", "sp-blockchain", "sp-core", + "sp-rpc", "sp-runtime", "sp-version", "thiserror", + "tokio", "tokio-stream", ] [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", "directories", "exit-future", - "futures", + "futures 0.3.30", "futures-timer", "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "pin-project", - "rand 0.8.5", - "sc-block-builder", + "rand", "sc-chain-spec", "sc-client-api", "sc-client-db", @@ -7176,11 +13466,9 @@ dependencies = [ "sc-network-light", "sc-network-sync", "sc-network-transactions", - "sc-offchain", "sc-rpc", "sc-rpc-server", "sc-rpc-spec-v2", - "sc-storage-monitor", "sc-sysinfo", "sc-telemetry", "sc-tracing", @@ -7215,39 +13503,56 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sp-core", ] [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "clap", "fs4", - "futures", "log", - "sc-client-db", - "sc-utils", "sp-core", "thiserror", "tokio", ] +[[package]] +name = "sc-sync-state-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec", + "sc-client-api", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-consensus-grandpa", + "serde", + "serde_json", + "sp-blockchain", + "sp-runtime", + "thiserror", +] + [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", + "derive_more", + "futures 0.3.30", "libc", "log", - "rand 0.8.5", + "rand", "rand_pcg", "regex", "sc-telemetry", @@ -7261,15 +13566,15 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "chrono", - "futures", + "futures 0.3.30", "libp2p", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "pin-project", - "rand 0.8.5", + "rand", "sc-utils", "serde", "serde_json", @@ -7280,20 +13585,19 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "ansi_term", - "atty", "chrono", + "is-terminal", "lazy_static", "libc", "log", - "once_cell", - "parking_lot 0.12.1", + "parity-scale-codec", + "parking_lot 0.12.2", "regex", "rustc-hash", "sc-client-api", - "sc-rpc-server", "sc-tracing-proc-macro", "serde", "sp-api", @@ -7311,27 +13615,26 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", - "futures", + "futures 0.3.30", "futures-timer", "linked-hash-map", "log", - "num-traits", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sc-client-api", "sc-transaction-pool-api", "sc-utils", @@ -7349,13 +13652,15 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", - "futures", + "futures 0.3.30", "log", + "parity-scale-codec", "serde", "sp-blockchain", + "sp-core", "sp-runtime", "thiserror", ] @@ -7363,23 +13668,23 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "async-channel", - "futures", + "async-channel 1.9.0", + "futures 0.3.30", "futures-timer", "lazy_static", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "prometheus", "sp-arithmetic", ] [[package]] name = "scale-info" -version = "2.6.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfdef77228a4c05dc94211441595746732131ad7f6530c6c18f045da7b7ab937" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "bitvec", "cfg-if", @@ -7391,11 +13696,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.6.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 1.0.109", @@ -7403,156 +13708,118 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - -[[package]] -name = "schelling-game-shared" -version = "4.0.0-dev" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-support-test", - "frame-system", - "num-integer", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "schelling-game-shared-link", - "sortition-sum-game", - "sortition-sum-game-link", - "sp-core", - "sp-io", - "sp-runtime", + "windows-sys 0.52.0", ] [[package]] -name = "schelling-game-shared-link" -version = "0.1.0" +name = "schnellru" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" dependencies = [ - "frame-support", - "parity-scale-codec", + "ahash 0.8.11", + "cfg-if", + "hashbrown 0.13.2", ] [[package]] -name = "schnellru" -version = "0.2.1" +name = "schnorrkel" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +checksum = "844b7645371e6ecdf61ff246ba1958c29e802881a749ae3fb1993675d210d28d" dependencies = [ - "ahash 0.8.7", - "cfg-if", - "hashbrown 0.13.2", + "arrayref", + "arrayvec 0.7.4", + "curve25519-dalek-ng", + "merlin", + "rand_core 0.6.4", + "sha2 0.9.9", + "subtle-ng", + "zeroize", ] [[package]] name = "schnorrkel" -version = "0.9.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" dependencies = [ + "aead", "arrayref", - "arrayvec 0.5.2", - "curve25519-dalek 2.1.3", - "getrandom 0.1.16", + "arrayvec 0.7.4", + "curve25519-dalek 4.1.2", + "getrandom_or_panic", "merlin", - "rand 0.7.3", - "rand_core 0.5.1", - "sha2 0.8.2", - "subtle", + "rand_core 0.6.4", + "serde_bytes", + "sha2 0.10.8", + "subtle 2.5.0", "zeroize", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" - -[[package]] -name = "sct" -version = "0.6.1" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "sdp" -version = "0.5.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "rand 0.8.5", - "substring", - "thiserror", - "url", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] name = "sec1" -version = "0.3.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", + "base16ct", + "der", "generic-array 0.14.7", - "pkcs8 0.9.0", - "subtle", + "pkcs8", + "subtle 2.5.0", "zeroize", ] [[package]] -name = "sec1" -version = "0.7.3" +name = "seccompiler" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5" dependencies = [ - "base16ct 0.2.0", - "der 0.7.5", - "generic-array 0.14.7", - "pkcs8 0.10.2", - "subtle", - "zeroize", + "libc", ] [[package]] name = "secp256k1" -version = "0.24.3" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.6.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] @@ -7568,11 +13835,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -7581,9 +13848,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -7609,9 +13876,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -7624,29 +13891,38 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -7655,13 +13931,26 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.1" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] +[[package]] +name = "services-payment-rpc" +version = "0.1.0" +dependencies = [ + "futures 0.3.30", + "jsonrpsee", + "pallet-services-payment-runtime-api", + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-runtime", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -7672,32 +13961,20 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha2" version = "0.9.9" @@ -7708,7 +13985,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -7724,9 +14001,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest 0.10.7", "keccak", @@ -7734,66 +14011,33 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] -[[package]] -name = "shared-storage" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "shared-storage-link", - "sp-core", - "sp-io", - "sp-runtime", -] - -[[package]] -name = "shared-storage-link" -version = "0.1.0" -dependencies = [ - "frame-support", - "parity-scale-codec", -] - [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -7812,17 +14056,42 @@ dependencies = [ "wide", ] +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +dependencies = [ + "bstr 0.2.17", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" +dependencies = [ + "console", + "similar", +] + +[[package]] +name = "simple-mermaid" +version = "0.1.0" +source = "git+https://github.com/kianenigma/simple-mermaid.git?rev=e48b187bcfd5cc75111acd9d241f1bd36604344b#e48b187bcfd5cc75111acd9d241f1bd36604344b" + [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -7833,45 +14102,195 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" +[[package]] +name = "slices" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2086e458a369cdca838e9f6ed04b4cc2e3ce636d99abb80c9e2eada107749cf" +dependencies = [ + "faster-hex", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "slot-range-helper" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "enumn", + "parity-scale-codec", + "paste", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smol" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" +dependencies = [ + "async-channel 1.9.0", + "async-executor", + "async-fs", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-net", + "async-process", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "smoldot" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0bb30cf57b7b5f6109ce17c3164445e2d6f270af2cb48f6e4d31c2967c9a9f5" +dependencies = [ + "arrayvec 0.7.4", + "async-lock 2.8.0", + "atomic-take", + "base64 0.21.7", + "bip39", + "blake2-rfc", + "bs58 0.5.1", + "chacha20", + "crossbeam-queue", + "derive_more", + "ed25519-zebra 4.0.3", + "either", + "event-listener 2.5.3", + "fnv", + "futures-lite 1.13.0", + "futures-util", + "hashbrown 0.14.5", + "hex", + "hmac 0.12.1", + "itertools 0.11.0", + "libsecp256k1", + "merlin", + "no-std-net", + "nom", + "num-bigint", + "num-rational", + "num-traits", + "pbkdf2 0.12.2", + "pin-project", + "poly1305", + "rand", + "rand_chacha 0.3.1", + "ruzstd", + "schnorrkel 0.10.2", + "serde", + "serde_json", + "sha2 0.10.8", + "sha3", + "siphasher", + "slab", + "smallvec", + "soketto", + "twox-hash", + "wasmi", + "x25519-dalek 2.0.1", + "zeroize", +] + +[[package]] +name = "smoldot-light" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "256b5bad1d6b49045e95fe87492ce73d5af81545d8b4d8318a872d2007024c33" +dependencies = [ + "async-channel 1.9.0", + "async-lock 2.8.0", + "base64 0.21.7", + "blake2-rfc", + "derive_more", + "either", + "event-listener 2.5.3", + "fnv", + "futures-channel", + "futures-lite 1.13.0", + "futures-util", + "hashbrown 0.14.5", + "hex", + "itertools 0.11.0", + "log", + "lru 0.11.1", + "no-std-net", + "parking_lot 0.12.2", + "pin-project", + "rand", + "rand_chacha 0.3.1", + "serde", + "serde_json", + "siphasher", + "slab", + "smol", + "smoldot", + "zeroize", +] [[package]] name = "snap" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "snow" -version = "0.9.4" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58021967fd0a5eeeb23b08df6cc244a4d4a5b4aec1d27c9e02fad1a58b4cd74e" +checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" dependencies = [ - "aes-gcm 0.10.1", - "blake2", + "aes-gcm", + "blake2 0.10.6", "chacha20poly1305", - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "rand_core 0.6.4", - "ring 0.17.5", + "ring 0.17.8", "rustc_version 0.4.0", "sha2 0.10.8", - "subtle", + "subtle 2.5.0", ] [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "soketto" version = "0.7.1" @@ -7881,41 +14300,18 @@ dependencies = [ "base64 0.13.1", "bytes", "flate2", - "futures", + "futures 0.3.30", "http", "httparse", "log", - "rand 0.8.5", + "rand", "sha-1", ] -[[package]] -name = "sortition-sum-game" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sortition-sum-game-link", - "sp-core", - "sp-io", - "sp-runtime", -] - -[[package]] -name = "sortition-sum-game-link" -version = "0.1.0" -dependencies = [ - "frame-support", - "parity-scale-codec", -] - [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "hash-db", "log", @@ -7923,6 +14319,7 @@ dependencies = [ "scale-info", "sp-api-proc-macro", "sp-core", + "sp-externalities", "sp-metadata-ir", "sp-runtime", "sp-state-machine", @@ -7935,21 +14332,21 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "Inflector", - "blake2", - "expander", - "proc-macro-crate", + "blake2 0.10.6", + "expander 2.1.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "sp-application-crypto" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "23.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", "scale-info", @@ -7960,25 +14357,37 @@ dependencies = [ ] [[package]] -name = "sp-arithmetic" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "sp-arithmetic" +version = "16.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "integer-sqrt", - "num-traits", "parity-scale-codec", "scale-info", - "serde", + "sp-api", + "sp-application-crypto", + "sp-runtime", "sp-std", - "static_assertions", ] [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "parity-scale-codec", "sp-api", "sp-inherents", "sp-runtime", @@ -7988,13 +14397,13 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", + "futures 0.3.30", "log", - "lru", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", + "schnellru", "sp-api", "sp-consensus", "sp-database", @@ -8006,10 +14415,10 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", - "futures", + "futures 0.3.30", "log", "sp-core", "sp-inherents", @@ -8021,14 +14430,13 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", "parity-scale-codec", "scale-info", "sp-api", "sp-application-crypto", - "sp-consensus", "sp-consensus-slots", "sp-inherents", "sp-runtime", @@ -8039,7 +14447,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", "parity-scale-codec", @@ -8047,20 +14455,37 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-consensus", "sp-consensus-slots", "sp-core", "sp-inherents", - "sp-keystore", "sp-runtime", "sp-std", "sp-timestamp", ] +[[package]] +name = "sp-consensus-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "lazy_static", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std", + "strum 0.24.1", +] + [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "finality-grandpa", "log", @@ -8078,7 +14503,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", "scale-info", @@ -8089,32 +14514,32 @@ dependencies = [ [[package]] name = "sp-core" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "21.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "array-bytes", - "bitflags", - "blake2", + "array-bytes 6.2.3", + "bip39", + "bitflags 1.3.2", + "blake2 0.10.6", "bounded-collections", - "bs58", + "bs58 0.5.1", "dyn-clonable", - "ed25519-zebra", - "futures", + "ed25519-zebra 3.1.0", + "futures 0.3.30", "hash-db", "hash256-std-hasher", "impl-serde", - "lazy_static", + "itertools 0.10.5", "libsecp256k1", "log", "merlin", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "paste", "primitive-types", - "rand 0.8.5", - "regex", + "rand", "scale-info", - "schnorrkel", + "schnorrkel 0.11.4", "secp256k1", "secrecy", "serde", @@ -8127,58 +14552,57 @@ dependencies = [ "ss58-registry", "substrate-bip39", "thiserror", - "tiny-bip39", + "tracing", + "w3f-bls", "zeroize", ] [[package]] name = "sp-core-hashing" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "9.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "blake2b_simd", "byteorder", "digest 0.10.7", "sha2 0.10.8", "sha3", - "sp-std", "twox-hash", ] [[package]] name = "sp-core-hashing-proc-macro" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "9.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "proc-macro2", "quote", "sp-core-hashing", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "kvdb", - "parking_lot 0.12.1", + "parking_lot 0.12.2", ] [[package]] name = "sp-debug-derive" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "8.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "sp-externalities" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "0.19.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "environmental", "parity-scale-codec", @@ -8186,16 +14610,26 @@ dependencies = [ "sp-storage", ] +[[package]] +name = "sp-genesis-builder" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "serde_json", + "sp-api", + "sp-runtime", + "sp-std", +] + [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-core", "sp-runtime", "sp-std", "thiserror", @@ -8203,13 +14637,11 @@ dependencies = [ [[package]] name = "sp-io" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "23.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "bytes", - "ed25519", "ed25519-dalek", - "futures", "libsecp256k1", "log", "parity-scale-codec", @@ -8229,24 +14661,21 @@ dependencies = [ [[package]] name = "sp-keyring" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "24.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "lazy_static", "sp-core", "sp-runtime", - "strum", + "strum 0.24.1", ] [[package]] name = "sp-keystore" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "0.27.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "futures", "parity-scale-codec", - "parking_lot 0.12.1", - "serde", + "parking_lot 0.12.2", "sp-core", "sp-externalities", "thiserror", @@ -8255,16 +14684,16 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "thiserror", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] name = "sp-metadata-ir" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -8272,10 +14701,40 @@ dependencies = [ "sp-std", ] +[[package]] +name = "sp-mixnet" +version = "0.1.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-std", +] + +[[package]] +name = "sp-mmr-primitives" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "ckb-merkle-mountain-range", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-debug-derive", + "sp-runtime", + "sp-std", + "thiserror", +] + [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", "scale-info", @@ -8289,7 +14748,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "sp-api", "sp-core", @@ -8298,8 +14757,8 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "8.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "backtrace", "lazy_static", @@ -8309,7 +14768,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "rustc-hash", "serde", @@ -8318,18 +14777,20 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "24.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "docify", "either", "hash256-std-hasher", "impl-trait-for-tuples", "log", "parity-scale-codec", "paste", - "rand 0.8.5", + "rand", "scale-info", "serde", + "simple-mermaid", "sp-application-crypto", "sp-arithmetic", "sp-core", @@ -8340,8 +14801,8 @@ dependencies = [ [[package]] name = "sp-runtime-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "17.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -8358,25 +14819,27 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "11.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "Inflector", - "proc-macro-crate", + "expander 2.1.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", "scale-info", "sp-api", "sp-core", + "sp-keystore", "sp-runtime", "sp-staking", "sp-std", @@ -8385,8 +14848,9 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", @@ -8397,14 +14861,14 @@ dependencies = [ [[package]] name = "sp-state-machine" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "0.28.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "hash-db", "log", "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", + "parking_lot 0.12.2", + "rand", "smallvec", "sp-core", "sp-externalities", @@ -8413,17 +14877,42 @@ dependencies = [ "sp-trie", "thiserror", "tracing", + "trie-db", +] + +[[package]] +name = "sp-statement-store" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "aes-gcm", + "curve25519-dalek 4.1.2", + "ed25519-dalek", + "hkdf", + "parity-scale-codec", + "rand", + "scale-info", + "sha2 0.10.8", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-externalities", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "thiserror", + "x25519-dalek 2.0.1", ] [[package]] name = "sp-std" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "8.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" [[package]] name = "sp-storage" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "13.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "impl-serde", "parity-scale-codec", @@ -8436,11 +14925,9 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", - "futures-timer", - "log", "parity-scale-codec", "sp-inherents", "sp-runtime", @@ -8450,8 +14937,8 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "10.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", "sp-std", @@ -8463,7 +14950,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "sp-api", "sp-runtime", @@ -8472,10 +14959,9 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", - "log", "parity-scale-codec", "scale-info", "sp-core", @@ -8487,20 +14973,21 @@ dependencies = [ [[package]] name = "sp-trie" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "22.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.11", "hash-db", - "hashbrown 0.13.2", "lazy_static", "memory-db", "nohash-hasher", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.2", + "rand", "scale-info", "schnellru", "sp-core", + "sp-externalities", "sp-std", "thiserror", "tracing", @@ -8510,8 +14997,8 @@ dependencies = [ [[package]] name = "sp-version" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "22.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "impl-serde", "parity-scale-codec", @@ -8527,40 +15014,39 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "8.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "sp-wasm-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "14.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", "sp-std", - "wasmi", "wasmtime", ] [[package]] name = "sp-weights" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "20.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ + "bounded-collections", "parity-scale-codec", "scale-info", "serde", "smallvec", "sp-arithmetic", - "sp-core", "sp-debug-derive", "sp-std", ] @@ -8576,43 +15062,157 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spinners" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab" +checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82" dependencies = [ "lazy_static", "maplit", - "strum", + "strum 0.24.1", ] [[package]] name = "spki" -version = "0.6.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.6.1", + "der", ] [[package]] -name = "spki" -version = "0.7.1" +name = "sqlformat" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" dependencies = [ - "base64ct", - "der 0.7.5", + "itertools 0.12.1", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash 0.8.11", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener 2.5.3", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.2.6", + "log", + "memchr", + "native-tls", + "once_cell", + "paste", + "percent-encoding", + "serde", + "sha2 0.10.8", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.8", + "sqlx-core", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "flume 0.11.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", + "urlencoding", ] [[package]] name = "ss58-registry" -version = "1.40.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b" +checksum = "4743ce898933fbff7bbf414f497c459a782d496269644b3d650a398ae6a487ba" dependencies = [ "Inflector", "num-format", @@ -8623,12 +15223,87 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "staging-parachain-info" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "staging-xcm" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "array-bytes 6.2.3", + "bounded-collections", + "derivative", + "environmental", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-weights", + "xcm-procedural", +] + +[[package]] +name = "staging-xcm-builder" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-transaction-payment", + "parity-scale-codec", + "polkadot-parachain-primitives", + "scale-info", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "staging-xcm-executor" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "environmental", + "frame-benchmarking", + "frame-support", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -8641,7 +15316,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg_aliases", "libc", "parking_lot 0.11.2", @@ -8663,11 +15338,38 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "stream-payment-rpc" +version = "0.1.0" +dependencies = [ + "futures 0.3.30", + "jsonrpsee", + "pallet-stream-payment-runtime-api", + "parity-scale-codec", + "serde", + "sp-api", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "strobe-rs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabb238a1cccccfa4c4fb703670c0d157e1256c1ba695abf1b93bd2bb14bab2d" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "keccak", + "subtle 2.5.0", + "zeroize", +] + [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -8675,7 +15377,16 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros", + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros 0.26.2", ] [[package]] @@ -8684,7 +15395,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -8692,33 +15403,27 @@ dependencies = [ ] [[package]] -name = "stun" -version = "0.4.4" +name = "strum_macros" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "base64 0.13.1", - "crc", - "lazy_static", - "md-5", - "rand 0.8.5", - "ring 0.16.20", - "subtle", - "thiserror", - "tokio", - "url", - "webrtc-util", + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.65", ] [[package]] name = "substrate-bip39" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +checksum = "6a7590dc041b9bc2825e52ce5af8416c73dbe9d0654402bfd4b4941938b94d8f" dependencies = [ "hmac 0.11.0", "pbkdf2 0.8.0", - "schnorrkel", + "schnorrkel 0.11.4", "sha2 0.9.9", "zeroize", ] @@ -8726,18 +15431,15 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "platforms 2.0.0", -] +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "frame-system-rpc-runtime-api", - "futures", + "futures 0.3.30", "jsonrpsee", "log", "parity-scale-codec", @@ -8753,7 +15455,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "hyper", "log", @@ -8765,7 +15467,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", "jsonrpsee", @@ -8775,151 +15477,476 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "substrate-state-trie-migration-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "sc-client-api", + "sc-rpc-api", + "serde", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-trie", + "trie-db", +] + +[[package]] +name = "substrate-test-client" +version = "2.0.1" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "array-bytes 6.2.3", + "async-trait", + "futures 0.3.30", + "parity-scale-codec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-executor", + "sc-offchain", + "sc-service", + "serde", + "serde_json", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-keyring", + "sp-keystore", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "substrate-test-runtime" +version = "2.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "array-bytes 6.2.3", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "log", + "pallet-babe", + "pallet-balances", + "pallet-timestamp", + "parity-scale-codec", + "sc-service", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-grandpa", + "sp-core", + "sp-externalities", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-std", + "sp-transaction-pool", + "sp-trie", + "sp-version", + "substrate-wasm-builder", + "trie-db", +] + +[[package]] +name = "substrate-test-runtime-client" +version = "2.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "futures 0.3.30", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "substrate-test-client", + "substrate-test-runtime", +] + [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "ansi_term", "build-helper", "cargo_metadata", "filetime", + "parity-wasm", "sp-maybe-compressed-blob", - "strum", + "strum 0.24.1", "tempfile", - "toml 0.7.3", + "toml 0.8.13", "walkdir", "wasm-opt", ] [[package]] -name = "substring" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tanssi-node" +version = "0.7.0" +dependencies = [ + "async-io 1.13.0", + "async-trait", + "ccp-authorities-noting-inherent", + "clap", + "cumulus-client-cli", + "cumulus-client-collator", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-consensus-proposer", + "cumulus-client-network", + "cumulus-client-parachain-inherent", + "cumulus-client-pov-recovery", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "dancebox-runtime", + "dc-orchestrator-chain-interface", + "dp-slot-duration-runtime-api", + "exit-future", + "flashbox-runtime", + "flume 0.10.14", + "frame-benchmarking", + "frame-benchmarking-cli", + "futures 0.3.30", + "jsonrpsee", + "log", + "manual-xcm-rpc", + "nimbus-consensus", + "nimbus-primitives", + "node-common", + "pallet-author-noting-runtime-api", + "pallet-collator-assignment-runtime-api", + "pallet-configuration", + "pallet-registrar-runtime-api", + "parity-scale-codec", + "polkadot-cli", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-service", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-manual-seal", + "sc-executor", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-offchain", + "sc-rpc", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "serde_json", + "services-payment-rpc", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-offchain", + "sp-panic-handler", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-timestamp", + "sp-transaction-pool", + "stream-payment-rpc", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", + "tc-consensus", + "tokio", + "tokio-util", + "tp-author-noting-inherent", + "tp-container-chain-genesis-data", + "try-runtime-cli", +] + +[[package]] +name = "tanssi-relay-encoder" +version = "0.1.0" dependencies = [ - "autocfg", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "frame-try-runtime", + "hex-literal 0.3.4", + "pallet-balances", + "parity-scale-codec", + "polkadot-runtime-parachains", + "rococo-runtime", + "rococo-runtime-constants", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] -name = "subtle" -version = "2.4.1" +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "syn" -version = "1.0.109" +name = "target-lexicon" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +name = "tc-consensus" +version = "0.1.0" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "async-backing-primitives", + "async-trait", + "cumulus-client-collator", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-consensus-proposer", + "cumulus-client-parachain-inherent", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-test-relay-sproof-builder", + "dp-consensus", + "fc-rpc", + "futures 0.3.30", + "futures-timer", + "log", + "nimbus-consensus", + "nimbus-primitives", + "pallet-registrar-runtime-api", + "parity-scale-codec", + "parking_lot 0.12.2", + "polkadot-core-primitives", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-parachain-primitives", + "polkadot-primitives", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-manual-seal", + "sc-consensus-slots", + "sc-keystore", + "sc-network-test", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keyring", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-timestamp", + "substrate-prometheus-endpoint", + "substrate-test-runtime-client", + "tempfile", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "tempfile" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", + "cfg-if", + "fastrand 2.1.0", + "rustix 0.38.34", + "windows-sys 0.52.0", ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", + "winapi-util", ] [[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "terminal_size" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "core-foundation-sys", - "libc", + "rustix 0.38.34", + "windows-sys 0.48.0", ] [[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "target-lexicon" -version = "0.12.7" +name = "termtree" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +name = "test-relay-sproof-builder" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/dancekit?branch=tanssi-polkadot-v1.6.0#ed1a0f0d7200bedab36f3b6294070c38502d8d87" dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix 0.37.19", - "windows-sys 0.45.0", + "cumulus-primitives-core", + "dp-collator-assignment", + "dp-core", + "frame-support", + "parity-scale-codec", + "sp-runtime", + "sp-state-machine", + "sp-trie", ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "thiserror" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ - "winapi-util", + "thiserror-impl", ] [[package]] -name = "termtree" -version = "0.4.1" +name = "thiserror-core" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "c001ee18b7e5e3f62cbf58c7fe220119e68d902bb7443179c0c8aef30090e999" +dependencies = [ + "thiserror-core-impl", +] [[package]] -name = "thiserror" -version = "1.0.40" +name = "thiserror-core-impl" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] @@ -8930,9 +15957,9 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -8947,11 +15974,35 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "thrift" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b82ca8f46f95b3ce96081fe3dd89160fdea970c254bb72925255d1b62aae692e" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float", + "threadpool", +] + +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619bfed27d807b54f7f776b9430d4f8060e66ee138a28632ca898584d462c31c" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + [[package]] name = "tikv-jemalloc-sys" -version = "0.5.3+5.3.0-patched" +version = "0.5.4+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" +checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" dependencies = [ "cc", "libc", @@ -8959,11 +16010,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ + "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -8971,46 +16025,27 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] [[package]] -name = "tiny-bip39" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" -dependencies = [ - "anyhow", - "hmac 0.12.1", - "once_cell", - "pbkdf2 0.11.0", - "rand 0.8.5", - "rustc-hash", - "sha2 0.10.8", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "serde", - "serde_json", + "crunchy", ] [[package]] @@ -9030,71 +16065,81 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", "mio", "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite 0.2.9", + "parking_lot 0.12.2", + "pin-project-lite 0.2.14", "signal-hook-registry", - "socket2", + "socket2 0.5.7", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", +] + +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand", + "tokio", ] [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.20.8", + "rustls 0.21.12", "tokio", - "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.14", "tokio", "tokio-util", ] [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.9", + "futures-util", + "hashbrown 0.14.5", + "pin-project-lite 0.2.14", "tokio", - "tracing", ] [[package]] @@ -9108,36 +16153,58 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.3" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ - "indexmap", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.8", ] [[package]] @@ -9153,63 +16220,137 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags", + "bitflags 2.5.0", "bytes", "futures-core", "futures-util", "http", "http-body", "http-range-header", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.14", "tower-layer", "tower-service", ] [[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tp-author-noting-inherent" +version = "0.1.0" +dependencies = [ + "async-trait", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "dp-core", + "frame-support", + "futures 0.3.30", + "hex-literal 0.3.4", + "log", + "parity-scale-codec", + "polkadot-primitives", + "sc-client-api", + "scale-info", + "sp-api", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-storage", + "sp-trie", + "test-relay-sproof-builder", +] + +[[package]] +name = "tp-container-chain-genesis-data" +version = "0.1.0" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "hex", + "hex-literal 0.3.4", + "log", + "parity-scale-codec", + "polkadot-primitives", + "scale-info", + "serde", + "serde_json", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "tp-traits", +] + +[[package]] +name = "tp-maths" +version = "0.1.0" +dependencies = [ + "sp-core", + "sp-runtime", +] + +[[package]] +name = "tp-traits" +version = "0.1.0" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.14", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -9225,14 +16366,37 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-gum" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "coarsetime", + "polkadot-primitives", + "tracing", + "tracing-gum-proc-macro", +] + +[[package]] +name = "tracing-gum-proc-macro" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "expander 2.1.0", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.65", +] + [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -9269,11 +16433,36 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trait-schelling-game-shared" +version = "0.1.0" +dependencies = [ + "frame-support", + "parity-scale-codec", + "sp-std", +] + +[[package]] +name = "trait-shared-storage" +version = "0.1.0" +dependencies = [ + "frame-support", + "parity-scale-codec", +] + +[[package]] +name = "trait-sortition-sum-game" +version = "0.1.0" +dependencies = [ + "frame-support", + "parity-scale-codec", +] + [[package]] name = "trie-db" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" +checksum = "ff28e0f815c2fea41ebddf148e008b077d2faddb026c9555b29696114d602642" dependencies = [ "hash-db", "hashbrown 0.13.2", @@ -9307,9 +16496,9 @@ dependencies = [ "idna 0.2.3", "ipnet", "lazy_static", - "rand 0.8.5", + "rand", "smallvec", - "socket2", + "socket2 0.4.10", "thiserror", "tinyvec", "tokio", @@ -9328,7 +16517,7 @@ dependencies = [ "ipconfig", "lazy_static", "lru-cache", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "resolv-conf", "smallvec", "thiserror", @@ -9339,14 +16528,14 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ "async-trait", "clap", @@ -9357,7 +16546,6 @@ dependencies = [ "parity-scale-codec", "sc-cli", "sc-executor", - "sc-service", "serde", "serde_json", "sp-api", @@ -9377,23 +16565,22 @@ dependencies = [ "sp-version", "sp-weights", "substrate-rpc-client", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] name = "trybuild" -version = "1.0.80" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501dbdbb99861e4ab6b60eb6a7493956a9defb644fd034bc4a5ef27c693c8a3a" +checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" dependencies = [ - "basic-toml", "dissimilar", "glob", - "once_cell", "serde", "serde_derive", "serde_json", "termcolor", + "toml 0.8.13", ] [[package]] @@ -9402,25 +16589,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" -[[package]] -name = "turn" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" -dependencies = [ - "async-trait", - "base64 0.13.1", - "futures", - "log", - "md-5", - "rand 0.8.5", - "ring 0.16.20", - "stun", - "thiserror", - "tokio", - "webrtc-util", -] - [[package]] name = "twox-hash" version = "1.6.3" @@ -9429,21 +16597,21 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand 0.8.5", + "rand", "static_assertions", ] [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -9459,15 +16627,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -9478,11 +16646,17 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "unicode-xid" @@ -9491,30 +16665,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "universal-hash" -version = "0.4.1" +name = "unicode_categories" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array 0.14.7", - "subtle", -] +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "universal-hash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", - "subtle", + "subtle 2.5.0", ] [[package]] name = "unsigned-varint" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" dependencies = [ "asynchronous-codec", "bytes", @@ -9536,29 +16706,26 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", ] [[package]] -name = "utf8parse" -version = "0.2.1" +name = "urlencoding" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] -name = "uuid" -version = "1.3.2" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" -dependencies = [ - "getrandom 0.2.11", -] +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "valuable" @@ -9585,25 +16752,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] -name = "waitgroup" -version = "0.1.2" +name = "w3f-bls" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +checksum = "7335e4c132c28cc43caef6adb339789e599e39adbe78da0c4d547fad48cbc331" dependencies = [ - "atomic-waker", + "ark-bls12-377", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-serialize-derive", + "arrayref", + "constcat", + "digest 0.10.7", + "rand", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "sha2 0.10.8", + "sha3", + "thiserror", + "zeroize", ] [[package]] name = "waker-fn" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -9611,11 +16793,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -9631,11 +16812,20 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasix" +version = "0.12.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -9643,24 +16833,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.65", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -9670,9 +16860,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9680,22 +16870,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.65", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-instrument" @@ -9708,14 +16898,14 @@ dependencies = [ [[package]] name = "wasm-opt" -version = "0.111.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a303793cbc01fb96551badfc7367db6007396bba6bac97936b3c8b6f7fdb41" +checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c" dependencies = [ "anyhow", "libc", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "tempfile", "thiserror", "wasm-opt-cxx-sys", @@ -9724,9 +16914,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.111.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c9deb56f8a9f2ec177b3bd642a8205621835944ed5da55f2388ef216aca5a4" +checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e" dependencies = [ "anyhow", "cxx", @@ -9736,15 +16926,14 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.111.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4432e28b542738a9776cedf92e8a99d8991c7b4667ee2c7ccddfb479dd2856a7" +checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe" dependencies = [ "anyhow", "cc", "cxx", "cxx-build", - "regex", ] [[package]] @@ -9753,7 +16942,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ - "futures", + "futures 0.3.30", "js-sys", "parking_lot 0.11.2", "pin-utils", @@ -9764,61 +16953,67 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.13.2" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +checksum = "77a8281d1d660cdf54c76a3efa9ddd0c270cada1383a995db3ccb43d166456c7" dependencies = [ - "parity-wasm", - "wasmi-validation", + "smallvec", + "spin 0.9.8", + "wasmi_arena", "wasmi_core", + "wasmparser-nostd", ] [[package]] -name = "wasmi-validation" -version = "0.5.0" +name = "wasmi_arena" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" -dependencies = [ - "parity-wasm", -] +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] name = "wasmi_core" -version = "0.2.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" dependencies = [ "downcast-rs", "libm", - "memory_units", - "num-rational", "num-traits", - "region", + "paste", ] [[package]] name = "wasmparser" -version = "0.100.0" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + [[package]] name = "wasmtime" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" dependencies = [ "anyhow", "bincode", "cfg-if", - "indexmap", + "indexmap 1.9.3", "libc", "log", - "object 0.29.0", + "object 0.30.4", "once_cell", "paste", "psm", @@ -9831,43 +17026,43 @@ dependencies = [ "wasmtime-environ", "wasmtime-jit", "wasmtime-runtime", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-asm-macros" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" +checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213" dependencies = [ "anyhow", - "base64 0.13.1", + "base64 0.21.7", "bincode", "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.13", + "rustix 0.36.17", "serde", "sha2 0.10.8", "toml 0.5.11", - "windows-sys 0.42.0", + "windows-sys 0.45.0", "zstd 0.11.2+zstd.1.5.2", ] [[package]] name = "wasmtime-cranelift" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" +checksum = "b1cefde0cce8cb700b1b21b6298a3837dba46521affd7b8c38a9ee2c869eee04" dependencies = [ "anyhow", "cranelift-codegen", @@ -9875,27 +17070,43 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.26.2", + "gimli 0.27.3", "log", - "object 0.29.0", + "object 0.30.4", "target-lexicon", "thiserror", "wasmparser", + "wasmtime-cranelift-shared", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd041e382ef5aea1b9fc78442394f1a4f6d676ce457e7076ca4cb3f397882f8b" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-native", + "gimli 0.27.3", + "object 0.30.4", + "target-lexicon", "wasmtime-environ", ] [[package]] name = "wasmtime-environ" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" dependencies = [ "anyhow", "cranelift-entity", - "gimli 0.26.2", - "indexmap", + "gimli 0.27.3", + "indexmap 1.9.3", "log", - "object 0.29.0", + "object 0.30.4", "serde", "target-lexicon", "thiserror", @@ -9905,18 +17116,18 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" dependencies = [ - "addr2line 0.17.0", + "addr2line 0.19.0", "anyhow", "bincode", "cfg-if", "cpp_demangle", - "gimli 0.26.2", + "gimli 0.27.3", "log", - "object 0.29.0", + "object 0.30.4", "rustc-demangle", "serde", "target-lexicon", @@ -9924,331 +17135,241 @@ dependencies = [ "wasmtime-jit-debug", "wasmtime-jit-icache-coherence", "wasmtime-runtime", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-jit-debug" -version = "6.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" -dependencies = [ - "object 0.29.0", - "once_cell", - "rustix 0.36.13", -] - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "6.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.42.0", -] - -[[package]] -name = "wasmtime-runtime" -version = "6.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" -dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap", - "libc", - "log", - "mach", - "memfd", - "memoffset 0.6.5", - "paste", - "rand 0.8.5", - "rustix 0.36.13", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", - "windows-sys 0.42.0", -] - -[[package]] -name = "wasmtime-types" -version = "6.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" -dependencies = [ - "cranelift-entity", - "serde", - "thiserror", - "wasmparser", -] - -[[package]] -name = "web-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki 0.22.0", -] - -[[package]] -name = "webrtc" -version = "0.6.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" -dependencies = [ - "arc-swap", - "async-trait", - "bytes", - "hex", - "interceptor", - "lazy_static", - "log", - "rand 0.8.5", - "rcgen 0.9.3", - "regex", - "ring 0.16.20", - "rtcp", - "rtp", - "rustls 0.19.1", - "sdp", - "serde", - "serde_json", - "sha2 0.10.8", - "stun", - "thiserror", - "time", - "tokio", - "turn", - "url", - "waitgroup", - "webrtc-data", - "webrtc-dtls", - "webrtc-ice", - "webrtc-mdns", - "webrtc-media", - "webrtc-sctp", - "webrtc-srtp", - "webrtc-util", +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" +dependencies = [ + "object 0.30.4", + "once_cell", + "rustix 0.36.17", ] [[package]] -name = "webrtc-data" -version = "0.6.0" +name = "wasmtime-jit-icache-coherence" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" dependencies = [ - "bytes", - "derive_builder", - "log", - "thiserror", - "tokio", - "webrtc-sctp", - "webrtc-util", + "cfg-if", + "libc", + "windows-sys 0.45.0", ] [[package]] -name = "webrtc-dtls" -version = "0.7.1" +name = "wasmtime-runtime" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" dependencies = [ - "aes 0.6.0", - "aes-gcm 0.10.1", - "async-trait", - "bincode", - "block-modes", - "byteorder", - "ccm", - "curve25519-dalek 3.2.0", - "der-parser 8.2.0", - "elliptic-curve 0.12.3", - "hkdf", - "hmac 0.12.1", + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", "log", - "oid-registry 0.6.1", - "p256", - "p384", - "rand 0.8.5", - "rand_core 0.6.4", - "rcgen 0.9.3", - "ring 0.16.20", - "rustls 0.19.1", - "sec1 0.3.0", - "serde", - "sha1", - "sha2 0.10.8", - "signature 1.6.4", - "subtle", - "thiserror", - "tokio", - "webpki 0.21.4", - "webrtc-util", - "x25519-dalek 2.0.0-pre.1", - "x509-parser 0.13.2", + "mach", + "memfd", + "memoffset", + "paste", + "rand", + "rustix 0.36.17", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.45.0", ] [[package]] -name = "webrtc-ice" -version = "0.9.1" +name = "wasmtime-types" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" dependencies = [ - "arc-swap", - "async-trait", - "crc", - "log", - "rand 0.8.5", + "cranelift-entity", "serde", - "serde_json", - "stun", "thiserror", - "tokio", - "turn", - "url", - "uuid", - "waitgroup", - "webrtc-mdns", - "webrtc-util", + "wasmparser", ] [[package]] -name = "webrtc-mdns" -version = "0.5.2" +name = "web-sys" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ - "log", - "socket2", - "thiserror", - "tokio", - "webrtc-util", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "webrtc-media" -version = "0.5.1" +name = "webpki" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72e1650a8ae006017d1a5280efb49e2610c19ccc3c0905b03b648aee9554991" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "byteorder", - "bytes", - "rand 0.8.5", - "rtp", - "thiserror", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] -name = "webrtc-sctp" -version = "0.7.0" +name = "webpki-roots" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "arc-swap", - "async-trait", - "bytes", - "crc", - "log", - "rand 0.8.5", - "thiserror", - "tokio", - "webrtc-util", + "webpki", ] [[package]] -name = "webrtc-srtp" -version = "0.9.1" +name = "webpki-roots" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "westend-runtime" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "aead 0.4.3", - "aes 0.7.5", - "aes-gcm 0.9.4", - "async-trait", - "byteorder", - "bytes", - "ctr 0.8.0", - "hmac 0.11.0", + "binary-merkle-tree", + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.4.1", "log", - "rtcp", - "rtp", - "sha-1", - "subtle", - "thiserror", - "tokio", - "webrtc-util", + "pallet-asset-rate", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-collective", + "pallet-conviction-voting", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-message-queue", + "pallet-mmr", + "pallet-multisig", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-preimage", + "pallet-proxy", + "pallet-recovery", + "pallet-referenda", + "pallet-root-testing", + "pallet-scheduler", + "pallet-session", + "pallet-session-benchmarking", + "pallet-society", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-staking-runtime-api", + "pallet-state-trie-migration", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-storage", + "sp-transaction-pool", + "sp-version", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", + "westend-runtime-constants", ] [[package]] -name = "webrtc-util" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +name = "westend-runtime-constants" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "async-trait", - "bitflags", - "bytes", - "cc", - "ipnet", - "lazy_static", - "libc", - "log", - "nix", - "rand 0.8.5", - "thiserror", - "tokio", - "winapi", + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", + "staging-xcm", + "staging-xcm-builder", ] [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix 0.38.34", ] [[package]] name = "wide" -version = "0.7.8" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" +checksum = "21e005a4cc35784183a9e39cb22e9a9c46353ef6a7f113fd8d36ddc58c15ef3c" dependencies = [ "bytemuck", "safe_arch", @@ -10256,9 +17377,9 @@ dependencies = [ [[package]] name = "widestring" -version = "0.5.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -10278,11 +17399,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -10293,39 +17414,30 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.34.0" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ - "windows_aarch64_msvc 0.34.0", - "windows_i686_gnu 0.34.0", - "windows_i686_msvc 0.34.0", - "windows_x86_64_gnu 0.34.0", - "windows_x86_64_msvc 0.34.0", + "windows-core 0.51.1", + "windows-targets 0.48.5", ] [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets 0.48.5", ] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.5", ] [[package]] @@ -10346,6 +17458,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -10376,6 +17497,22 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -10389,10 +17526,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "windows_aarch64_msvc" -version = "0.34.0" +name = "windows_aarch64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -10407,10 +17544,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] -name = "windows_i686_gnu" -version = "0.34.0" +name = "windows_aarch64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -10425,10 +17562,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] -name = "windows_i686_msvc" -version = "0.34.0" +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -10443,10 +17586,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "windows_x86_64_gnu" -version = "0.34.0" +name = "windows_i686_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -10460,6 +17603,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -10473,10 +17622,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "windows_x86_64_msvc" -version = "0.34.0" +name = "windows_x86_64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -10490,22 +17639,38 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -10530,50 +17695,86 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.0-pre.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 3.2.0", + "curve25519-dalek 4.1.2", "rand_core 0.6.4", + "serde", "zeroize", ] [[package]] name = "x509-parser" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ - "asn1-rs 0.3.1", + "asn1-rs", "base64 0.13.1", "data-encoding", - "der-parser 7.0.0", + "der-parser", "lazy_static", "nom", - "oid-registry 0.4.0", - "ring 0.16.20", + "oid-registry", "rusticata-macros", "thiserror", "time", ] [[package]] -name = "x509-parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +name = "xcm-emulator" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" dependencies = [ - "asn1-rs 0.5.2", - "base64 0.13.1", - "data-encoding", - "der-parser 8.2.0", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "impl-trait-for-tuples", "lazy_static", - "nom", - "oid-registry 0.6.1", - "rusticata-macros", - "thiserror", - "time", + "log", + "pallet-balances", + "pallet-message-queue", + "parachains-common", + "parity-scale-codec", + "paste", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "sp-arithmetic", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "xcm-primitives" +version = "0.1.0" +source = "git+https://github.com/moondance-labs/moonkit?branch=tanssi-polkadot-v1.6.0#070849b6c2d71401ef5de9bdb0f4af17ed998244" +dependencies = [ + "sp-runtime", +] + +[[package]] +name = "xcm-procedural" +version = "1.0.0" +source = "git+https://github.com/moondance-labs/polkadot-sdk?branch=tanssi-polkadot-v1.6.0#7b16c9ecbe06c53affbbb32991875b0c1e5f59f6" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.65", ] [[package]] @@ -10582,11 +17783,11 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" dependencies = [ - "futures", + "futures 0.3.30", "log", "nohash-hasher", - "parking_lot 0.12.1", - "rand 0.8.5", + "parking_lot 0.12.2", + "rand", "static_assertions", ] @@ -10607,29 +17808,29 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -10642,7 +17843,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.65", ] [[package]] @@ -10656,11 +17857,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", + "zstd-safe 6.0.6", ] [[package]] @@ -10675,9 +17876,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", @@ -10685,11 +17886,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 7bc97dc..cbebc09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,411 @@ [workspace] members = [ - "node", - "pallets/template", - "pallets/sortition-sum-game", - "pallets/schelling-game-shared", - "pallets/shared-storage", - "pallets/spaces", - "pallets/profile-validation", - "pallets/positive-externality", - "pallets/support", - "pallets/election", - "pallets/department-funding", - "pallets/project-tips", - "pallets/posts", - "pallets/ubi", - "pallets/tags", - "traits/sortition-sum-game-link", - "traits/schelling-game-shared-link", - "runtime/node-runtime", + "client/*", + "container-chains/nodes/*", + "container-chains/runtime-templates/*", + "node", + "pallets/*", + "custom-pallets/department-funding", + "custom-pallets/positive-externality", + "custom-pallets/profile-validation", + "custom-pallets/project-tips", + "custom-pallets/schelling-game-shared", + "custom-pallets/shared-storage", + "custom-pallets/sortition-sum-game", + "custom-pallets/spaces", + "custom-pallets/support", + "pallets/collator-assignment/rpc/runtime-api", + "pallets/registrar/rpc/runtime-api", + "primitives/*", + "runtime/dancebox", + "runtime/flashbox", + "runtime/relay-encoder", ] +resolver = "2" + +[workspace.package] +authors = ["Moondance Labs"] +repository = "https://github.com/moondance-labs/tanssi" + +[workspace.lints.clippy] +# Deny main lint groups +complexity = { level = "deny", priority = 1 } +correctness = { level = "deny", priority = 1 } +suspicious = { level = "deny", priority = 1 } + +# Add some additional lints +as_underscore = { level = "warn", priority = 1 } +cast_lossless = { level = "warn", priority = 1 } +cast_possible_wrap = { level = "warn", priority = 1 } +cast_precision_loss = { level = "warn", priority = 1 } +cast_sign_loss = { level = "warn", priority = 1 } +debug_assert_with_mut_call = { level = "warn", priority = 1 } +fn_to_numeric_cast_any = { level = "warn", priority = 1 } +invalid_upcast_comparisons = { level = "warn", priority = 1 } + +# Allow annoying lints and false positives +erasing_op = { level = "allow", priority = 2 } +identity_op = { level = "allow", priority = 2 } +too-many-arguments = { level = "allow", priority = 2 } +type_complexity = { level = "allow", priority = 2 } + +[workspace.lints.rust] +unsafe-code = { level = "deny", priority = 1 } + +[workspace.dependencies] + +# Members +pallet-author-noting = { path = "pallets/author-noting", default-features = false } +pallet-author-noting-runtime-api = { path = "pallets/author-noting/rpc/runtime-api", default-features = false } +pallet-authority-assignment = { path = "pallets/authority-assignment", default-features = false } +pallet-authority-mapping = { path = "pallets/authority-mapping", default-features = false } +pallet-collator-assignment = { path = "pallets/collator-assignment", default-features = false } +pallet-collator-assignment-runtime-api = { path = "pallets/collator-assignment/rpc/runtime-api", default-features = false } +pallet-configuration = { path = "pallets/configuration", default-features = false } +pallet-data-preservers = { path = "pallets/data-preservers", default-features = false } +pallet-inflation-rewards = { path = "pallets/inflation-rewards", default-features = false } +pallet-initializer = { path = "pallets/initializer", default-features = false } +pallet-invulnerables = { path = "pallets/invulnerables", default-features = false } +pallet-pooled-staking = { path = "pallets/pooled-staking", default-features = false } +pallet-registrar = { path = "pallets/registrar", default-features = false } +pallet-registrar-runtime-api = { path = "pallets/registrar/rpc/runtime-api", default-features = false } +pallet-services-payment = { path = "pallets/services-payment", default-features = false } +pallet-services-payment-runtime-api = { path = "pallets/services-payment/rpc/runtime-api", default-features = false } +pallet-stream-payment = { path = "pallets/stream-payment", default-features = false } +pallet-stream-payment-runtime-api = { path = "pallets/stream-payment/rpc/runtime-api", default-features = false } +pallet-xcm-core-buyer = { path = "pallets/xcm-core-buyer", default-features = false } + +## New start + +## New pallets +pallet-template = { path = "custom-pallets/template", default-features = false } +pallet-support = { path = "custom-pallets/support", default-features = false } +pallet-spaces = { path = "custom-pallets/spaces", default-features = false } +pallet-sortition-sum-game = { path = "custom-pallets/sortition-sum-game", default-features = false } +pallet-shared-storage = { path = "custom-pallets/shared-storage", default-features = false } +pallet-schelling-game-shared = { path = "custom-pallets/schelling-game-shared", default-features = false } +pallet-profile-validation = { path = "custom-pallets/profile-validation", default-features = false } +pallet-project-tips = { path = "custom-pallets/project-tips", default-features = false } +pallet-positive-externality = { path = "custom-pallets/positive-externality", default-features = false } +pallet-department-funding = { path = "custom-pallets/department-funding", default-features = false } + +## Traits +trait-sortition-sum-game = { path = "traits/trait-sortition-sum-game", default-features = false } +trait-shared-storage = { path = "traits/trait-shared-storage", default-features = false } +trait-schelling-game-shared = { path = "traits/trait-schelling-game-shared", default-features = false } + +## Api +profile-validation-runtime-api = { path = "custom-pallets/profile-validation/profile-validation-runtime-api", default-features = false } +project-tips-runtime-api = { path = "custom-pallets/project-tips/project-tips-runtime-api", default-features = false } +positive-externality-runtime-api = { path = "custom-pallets/positive-externality/positive-externality-runtime-api", default-features = false } +department-funding-runtime-api = { path = "custom-pallets/department-funding/department-funding-runtime-api", default-features = false } + + +## Rpc +profile-validation-rpc = { path = "custom-pallets/profile-validation/profile-validation-rpc", default-features = false } +project-tips-rpc = { path = "custom-pallets/project-tips/project-tips-rpc", default-features = false } +positive-externality-rpc = { path = "custom-pallets/positive-externality/positive-externality-rpc", default-features = false } +department-funding-rpc = { path = "custom-pallets/department-funding/department-funding-rpc", default-features = false } + + +## Additional dependancies +sp-arithmetic = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +strum = { version = "0.26.2", default-features = false, features = ["derive"] } +num-integer = { default-features = false, version = "0.1.44" } +frame-support-test = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-insecure-randomness-collective-flip = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-npos-elections = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +## New end + + +container-chain-template-frontier-runtime = { path = "container-chains/runtime-templates/frontier", default-features = false } +container-chain-template-simple-runtime = { path = "container-chains/runtime-templates/simple", default-features = false } + +dancebox-runtime = { path = "runtime/dancebox", default-features = false } +flashbox-runtime = { path = "runtime/flashbox", default-features = false } +manual-xcm-rpc = { path = "client/manual-xcm" } +node-common = { path = "client/node-common" } +runtime-common = { path = "runtime/common", default-features = false } +services-payment-rpc = { path = "client/services-payment" } +stream-payment-rpc = { path = "client/stream-payment" } +tanssi-relay-encoder = { path = "runtime/relay-encoder", default-features = false } +tc-consensus = { path = "client/consensus" } +tp-author-noting-inherent = { path = "primitives/author-noting-inherent", default-features = false } +tp-container-chain-genesis-data = { path = "primitives/container-chain-genesis-data", default-features = false } +tp-fungibles-ext = { path = "primitives/fungibles-ext", default-features = false } +tp-maths = { path = "primitives/maths", default-features = false } +tp-traits = { path = "primitives/traits", default-features = false } + +# Dancekit (wasm) +ccp-authorities-noting-inherent = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +ccp-xcm = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +dp-chain-state-snapshot = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +dp-collator-assignment = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +dp-consensus = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +dp-impl-tanssi-pallets-config = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +dp-slot-duration-runtime-api = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +dp-core = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-cc-authorities-noting = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-xcm-executor-utils = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +test-relay-sproof-builder = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# Dancekit (client) +dc-orchestrator-chain-interface = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0" } +dc-orchestrator-chain-rpc-interface = { git = "https://github.com/moondance-labs/dancekit", branch = "tanssi-polkadot-v1.6.0" } + +# Moonkit (wasm) +async-backing-primitives = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +nimbus-consensus = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0" } +nimbus-primitives = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-async-backing = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-author-inherent = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompile-balances-erc20 = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompile-batch = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompile-call-permit = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompile-xcm-utils = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompileset-assets-erc20 = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-foreign-asset-creator = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-maintenance-mode = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-migrations = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-relay-storage-roots = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } +xcm-primitives = { git = "https://github.com/moondance-labs/moonkit", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# Substrate (wasm) +frame-benchmarking = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +frame-executive = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +frame-support = { git = "https://github.com/moondance-labs/polkadot-sdk.git", branch = "tanssi-polkadot-v1.6.0", version = "4.0.0-dev", default-features = false } +frame-system = { git = "https://github.com/moondance-labs/polkadot-sdk.git", branch = "tanssi-polkadot-v1.6.0", version = "4.0.0-dev", default-features = false } +frame-system-benchmarking = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +frame-try-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-asset-rate = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-assets = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-balances = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-identity = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-message-queue = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-multisig = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-proxy = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-root-testing = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-session = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-staking = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-sudo = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-timestamp = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-transaction-payment = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-treasury = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-tx-pause = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-utility = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +parity-scale-codec = { version = "3.0.0", default-features = false, features = [ + "derive", + "max-encoded-len", +] } +scale-info = { version = "2.10.0", default-features = false } +sp-api = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-application-crypto = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-block-builder = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-consensus = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-consensus-aura = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-consensus-babe = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-consensus-beefy = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-consensus-slots = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-core = { git = "https://github.com/moondance-labs/polkadot-sdk.git", branch = "tanssi-polkadot-v1.6.0", version = "21.0.0", default-features = false } +sp-debug-derive = { git = "https://github.com/moondance-labs/polkadot-sdk.git", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-inherents = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-io = { git = "https://github.com/moondance-labs/polkadot-sdk.git", branch = "tanssi-polkadot-v1.6.0", version = "23.0.0", default-features = false } +sp-keyring = { git = "https://github.com/moondance-labs/polkadot-sdk.git", branch = "tanssi-polkadot-v1.6.0", version = "24.0.0", default-features = false } +sp-offchain = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-panic-handler = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk.git", branch = "tanssi-polkadot-v1.6.0", version = "24.0.0", default-features = false } +sp-session = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-state-machine = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-std = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-transaction-pool = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-trie = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-version = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# Substrate (client) +frame-benchmarking-cli = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +pallet-transaction-payment-rpc = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sc-basic-authorship = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-block-builder = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-chain-spec = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-cli = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-client-api = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-consensus = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-consensus-aura = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-consensus-grandpa = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-consensus-manual-seal = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-consensus-slots = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-executor = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-keystore = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-network = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-network-common = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-network-sync = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-network-test = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-network-transactions = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-offchain = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-rpc = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-service = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-sysinfo = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-telemetry = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-tracing = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-transaction-pool = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-transaction-pool-api = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sc-utils = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sp-blockchain = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +sp-externalities = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-genesis-builder = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-keystore = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-staking = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-storage = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +sp-timestamp = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +substrate-build-script-utils = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +substrate-frame-rpc-system = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +substrate-prometheus-endpoint = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +substrate-test-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +substrate-test-runtime-client = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +substrate-wasm-builder = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +try-runtime-cli = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } + +# Polkadot (wasm) +pallet-xcm = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-xcm-benchmarks = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +polkadot-core-primitives = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +polkadot-node-primitives = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +polkadot-parachain-primitives = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +polkadot-runtime-common = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +polkadot-runtime-parachains = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +rococo-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +rococo-runtime-constants = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +staging-xcm = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +staging-xcm-builder = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +staging-xcm-executor = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +westend-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +westend-runtime-constants = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# Polkadot (client) +polkadot-cli = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +polkadot-node-subsystem = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +polkadot-overseer = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +polkadot-primitives = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +polkadot-service = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } + +# Cumulus (wasm) +cumulus-pallet-dmp-queue = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-pallet-parachain-system = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false, features = [ + "parameterized-consensus-hook", +] } +cumulus-pallet-session-benchmarking = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-pallet-xcm = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-pallet-xcmp-queue = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-primitives-core = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-primitives-timestamp = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-primitives-utility = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +parachain-info = { package = "staging-parachain-info", git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +parachains-common = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# Cumulus (client) +assets-common = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +cumulus-client-cli = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-collator = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-consensus-aura = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-consensus-common = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-consensus-proposer = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-network = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-parachain-inherent = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-pov-recovery = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-client-service = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-primitives-parachain-inherent = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-relay-chain-interface = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +cumulus-test-relay-sproof-builder = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +emulated-integration-tests-common = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0" } +xcm-emulator = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# Frontier (wasm) +fp-account = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fp-evm = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fp-rpc = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fp-self-contained = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-base-fee = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-ethereum = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-chain-id = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompile-modexp = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompile-sha3fips = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-evm-precompile-simple = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +pallet-hotfix-sufficients = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +precompile-utils = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# Frontier (client) +fc-api = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fc-cli = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fc-consensus = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fc-db = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fc-mapping-sync = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fc-rpc = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", features = [ + "rpc-binary-search-estimate", +] } +fc-rpc-core = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } +fc-storage = { git = "https://github.com/moondance-labs/frontier", branch = "tanssi-polkadot-v1.6.0", default-features = false } + +# General (wasm) +bounded-collections = { version = "0.1.8", default-features = false } +hex-literal = { version = "0.3.4" } +impl-trait-for-tuples = "0.2.2" +impls = "1.0.3" +log = { version = "0.4.17", default-features = false } +num_enum = { version = "0.7.1", default-features = false } +rand_chacha = { version = "0.3.1", default-features = false } +serde = { version = "1.0.152", default-features = false } +smallvec = "1.10.0" +tap = "1.0.1" + +# General (client) +async-io = "1.3" +async-trait = "0.1" +clap = { version = "4.1.6", default-features = false, features = ["derive"] } +core_extensions = "1.5.3" +exit-future = { version = "0.2.0" } +flume = "0.10.9" +futures = { version = "0.3.1" } +futures-timer = "3.0.1" +hex = { version = "0.4.3", default-features = false } +jsonrpsee = { version = "0.16.2", features = [ + "client-core", + "server", + "macros", +] } +num-traits = "0.2.8" +parking_lot = "0.12.1" +paste = "1.0.14" +rand = { version = "0.8.5", default-features = false, features = ["std_rng"] } +serde_json = { version = "1.0.96", default-features = false } +similar-asserts = "1.1.0" +tempfile = "3.1.0" +thiserror = { version = "1.0.40" } +tokio = { version = "1.32.0", default-features = false } +tokio-util = { version = "0.7.10", default-features = false } +tracing = { version = "0.1.37", default-features = false } +tracing-subscriber = { version = "0.2.25", default-features = false } +url = "2.2.2" + +[patch.crates-io] +jsonrpsee = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } +jsonrpsee-client-transport = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } +jsonrpsee-core = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } +jsonrpsee-http-client = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } +jsonrpsee-proc-macros = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } +jsonrpsee-server = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } +jsonrpsee-types = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } +jsonrpsee-ws-client = { git = "https://github.com/moondance-labs/jsonrpsee", branch = "tanssi-polkadot-v1.1.0" } + +[profile.production] +codegen-units = 1 +inherits = "release" +lto = true + + [profile.release] +opt-level = 3 panic = "unwind" diff --git a/Containerfile b/Containerfile deleted file mode 100644 index 01a4a54..0000000 --- a/Containerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM docker.io/library/ubuntu:22.04 - -# show backtraces -ENV RUST_BACKTRACE 1 - -# install tools and dependencies -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - ca-certificates && \ -# apt cleanup - apt-get autoremove -y && \ - apt-get clean && \ - find /var/lib/apt/lists/ -type f -not -name lock -delete; \ -# add user and link ~/.local/share/polkadot to /data - useradd -m -u 1000 -U -s /bin/sh -d /polkadot polkadot && \ - mkdir -p /data /polkadot/.local/share && \ - chown -R polkadot:polkadot /data && \ - ln -s /data /polkadot/.local/share/node-template - -USER polkadot - -# copy the compiled binary to the container -COPY --chown=polkadot:polkadot --chmod=774 node-template /usr/bin/node-template - -# check if executable works in this container -RUN /usr/bin/node-template --version - -# ws_port -EXPOSE 9930 9333 9944 30333 30334 - -CMD ["/usr/bin/node-template"] diff --git a/LICENSE b/LICENSE index 80750e8..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,674 @@ -Copyright (c) 2022 Amiya Behera - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 6848498..0c47c08 100644 --- a/README.md +++ b/README.md @@ -1,265 +1,169 @@ -# Shivarthu +

+ +

-### Decentralized democracy with experts as leaders. +**A permissionless appchain infrastructure protocol designed for swift and effortless deployment of application-specific blockchains** -https://shivarthu.reaudito.com/#/ +🔎 For more about Tanssi Network, head to our [website](https://www.tanssi.network)
+📢 Follow our latest updates on [Twitter](https://twitter.com/TanssiNetwork)
+🤝 Engage with fellow developers on our [Discord server](https://discord.com/invite/kuyPhew2KB)
-## Leptos/Rust Frontend +## Build the Tanssi Node -https://github.com/reaudito-org/shivarthu-client +To build Tanssi, you will need a proper Substrate development environment. -## React Frontend +If you need a refresher setting up your Substrate environment, see [Substrate's Getting Started Guide](https://substrate.dev/docs/en/knowledgebase/getting-started/). -https://github.com/amiyatulu/shivarthu_frontend - -## Whitepaper - -https://shivarthu.reaudito.com/paper/Shivarthu_whitepaper.pdf - -## Technical Details - -https://github.com/reaudito-org/shivarthu/blob/main/docs/Shivarthu.md - - - -# Substrate Node Template - -[![Try on playground](https://img.shields.io/badge/Playground-Node_Template-brightgreen?logo=Parity%20Substrate)](https://playground.substrate.dev/?deploy=node-template) [![Matrix](https://img.shields.io/matrix/substrate-technical:matrix.org)](https://matrix.to/#/#substrate-technical:matrix.org) - -A fresh FRAME-based [Substrate](https://www.substrate.io/) node, ready for hacking :rocket: - -## Getting Started - -Follow the steps below to get started with the Node Template, or get it up and running right from your browser -in just a few clicks using [Playground](https://playground.substrate.dev/) :hammer_and_wrench: - -### Using Nix - -Install [nix](https://nixos.org/) and optionally [direnv](https://github.com/direnv/direnv) and [lorri](https://github.com/target/lorri) for a fully plug -and play experience for setting up the development environment. To get all the correct dependencies activate direnv `direnv allow` and lorri `lorri shell`. - -### Rust Setup +```bash +# Fetch the code +git clone https://github.com/moondance-labs/tanssi +cd tanssi -First, complete the [basic Rust setup instructions](./docs/rust-setup.md). +# Build the node (The first build will be long (~30min)) +cargo build --release +``` -### Run +## Run tests -Use Rust's native `cargo` command to build and launch the template node: +Tanssi has Rust unit tests as well as typescript integration tests. These tests are run in CI, and can also be run locally. Tanssi tests (specially those in typescript) depend on sessions being shorter, so you probably want to compile the node first as: -```sh -cargo run --release -- --dev --tmp +```bash +# Build the node with short session times +cargo build --features=fast-runtime --release ``` -### Build - -The `cargo run` command will perform an initial build. Use the following command to build the node -without launching it: +Then to run the tests: -```sh -cargo build --release +```bash +# Run the Rust unit tests +cargo test --features=fast-runtime --release ``` -### Test +Typescript tests are run with [Moonwall](https://github.com/Moonsong-Labs/moonwall). To run these you will need to have pnpm installed: -Use the following command to test the contract -```sh -cargo test -``` +```bash +# Install moonwall +sudo npm i -g pnpm -### Generate docs +# Install dependencies +pnpm i -To generate docs use command -```sh -cargo doc --no-deps --open -``` +# Run manual seal orchestrator tests +pnpm moonwall test dev_tanssi -To view in the python server, go to target folder -```sh -cd target/doc -python3 -m http.server +# Run zombienet tests (with container-chains) +pnpm moonwall test zombie_tanssi ``` -View the docs at: -http://localhost:8000/election/ - -Schelling game shared docs: -http://localhost:8000/schelling_game_shared/pallet/struct.Pallet.html -### Embedded Docs +Moonwall lets you also run the testing environment wihtout performing any tests on it, as a method for you to manually test certain things: -Once the project has been built, the following command can be used to explore all parameters and -subcommands: +```bash +# Spin up single manual-seal orchestrator +pnpm moonwall run dev_tanssi -```sh -./target/release/node-template -h +# Spin up orchestrator and two container-chains with zombienet +pnpm moonwall run zombie_tanssi ``` -## Run - -The provided `cargo run` command will launch a temporary node and its state will be discarded after -you terminate the process. After the project has been built, there are other ways to launch the -node. - -### Single-Node Development Chain +### Sealing options -This command will start the single-node development chain with persistent state: +The command above will start the node in instant seal mode. It creates a block when a transaction arrives, similar to Ganache's auto-mine. You can also choose to author blocks at a regular interval, or control authoring manually through the RPC. ```bash -./target/release/node-template --dev -``` - -Purge the development chain's state: +# Author a block every 6 seconds. +./target/release/tanssi-node --dev --sealing 6000 -```bash -./target/release/node-template purge-chain --dev +# Manually control the block authorship and finality +./target/release/tanssi-node --dev --sealing manual ``` -Start the development chain with detailed logging: +### Prefunded Development Addresses -```bash -RUST_BACKTRACE=1 ./target/release/node-template -ldebug --dev -``` +Running Tanssi in development mode will pre-fund several well-known addresses that (mostly) These addresses are derived from +using the well known private key `bottom drive obey lake curtain smoke basket hold race lonely fit walk` and appending the account name as a hard derivation key to the seed above, e.g., `bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice`: -### Connect with Yew Apps Front-end +``` +# Alice: +- Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY -The front end repository: https://github.com/amiyatulu/shivarthu_client +# Bob: +- Address: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty -```bash -cd shivarthu_client -trunk serve -``` +# Charlie: +- Address: 5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y -### Substrate explorer +# Dave: +- Address: 5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy - +# Eve: +- Address: 5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw -### Chain Specification +# Ferdie: +- Address: 5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL -```bash -./target/release/node-template build-spec > chain_spec.json ``` +## Runtime Architecture -### Raw Chain Spec +The Tanssi Runtime is built using FRAME and consists of pallets from substrate, frontier, cumulus, and `pallets/`. -Generating a Raw JSON Chain Specification File +From substrate: -One final step before deploying the Tanssi appchain is converting the JSON specification file to a raw format, which is a compact, less-readable version of the same file, required to initialize a node. +- _Balances_: Tracks token balances +- _Sudo_: Allows a privileged account to make arbitrary runtime changes - will be removed before + launch +- _Timestamp_: On-Chain notion of time +- _Transaction Payment_: Transaction payment (fee) management +- _Authorship_: A pallet where authorship information for orchestrator is stored +- _Invulnerables_: A pallet that selects invulnerable collators to be assigned to author in container-chains and orchestrator +- _Session_: A pallet that handles session-changes and keys +- _AuthorityMapping_: A pallet that handles a mapping between collator accounts and authority keys -```bash -./target/release/node-template build-spec --chain=chain_spec.json --raw > raw_chain_spec.json -``` +From cumulus: +- _ParachainSystem_: A helper to perform relay-storage verifications and injection of cross-chain messages +- _ParachainInfo_: A place to store parachain-relevant constants like parachain id -### Multi-Node Local Testnet +The following pallets are stored in `pallets/`. They are designed for Tanssi's specific requirements: -If you want to see the multi-node consensus algorithm in action, refer to -[our Start a Private Network tutorial](https://substrate.dev/docs/en/tutorials/start-a-private-network/). +- _Registrar_: A pallet that stores all registered container-chains +- _Configuration_: A pallet storing the current configuration from which several other components depend +- _CollatorAssignment_: A pallet implementing collator account to orchestrator/container-chain assignment +- _AuthorityAssignment_: A pallet implementing collator authority key to orchestrator/container-chain assignment +- _Initializer_: A pallet that handles everything that happens on a session-change +- _AuthorNoting_: A pallet that stores the latest author of each of the container-chains -## Template Structure +When modifying the git repository for these dependencies, a tool called [diener](https://github.com/bkchr/diener) can be used to replace the git URL and branch for each reference in all `Cargo.toml` files with a single command. This alleviates a lot of the repetitive modifications necessary when changing dependency versions. -A Substrate project such as this consists of a number of components that are spread across a few -directories. +## Container-chain templates -### Node +Currently two templates are offered within this repository -A blockchain node is an application that allows users to participate in a blockchain network. -Substrate-based blockchain nodes expose a number of capabilities: -- Networking: Substrate nodes use the [`libp2p`](https://libp2p.io/) networking stack to allow the - nodes in the network to communicate with one another. -- Consensus: Blockchains must have a way to come to - [consensus](https://substrate.dev/docs/en/knowledgebase/advanced/consensus) on the state of the - network. Substrate makes it possible to supply custom consensus engines and also ships with - several consensus mechanisms that have been built on top of - [Web3 Foundation research](https://research.web3.foundation/en/latest/polkadot/NPoS/index.html). -- RPC Server: A remote procedure call (RPC) server is used to interact with Substrate nodes. +- __Simple template__: Which ressembles the parachain-template node from cumulus and substrate, and only basic pallet like *pallet-balances*, *parachain-system* and basic configuration. -There are several files in the `node` directory - take special note of the following: +- __Frontier template__: Which ressembles a moonbeam-alike chain, with all pallets necessary for evm and ethereum compatibility -- [`chain_spec.rs`](./node/src/chain_spec.rs): A - [chain specification](https://substrate.dev/docs/en/knowledgebase/integrate/chain-spec) is a - source code file that defines a Substrate chain's initial (genesis) state. Chain specifications - are useful for development and testing, and critical when architecting the launch of a - production chain. Take note of the `development_config` and `testnet_genesis` functions, which - are used to define the genesis state for the local development chain configuration. These - functions identify some - [well-known accounts](https://substrate.dev/docs/en/knowledgebase/integrate/subkey#well-known-keys) - and use them to configure the blockchain's initial state. -- [`service.rs`](./node/src/service.rs): This file defines the node implementation. Take note of - the libraries that this file imports and the names of the functions it invokes. In particular, - there are references to consensus-related topics, such as the - [longest chain rule](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#longest-chain-rule), - the [Aura](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#aura) block authoring - mechanism and the - [GRANDPA](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#grandpa) finality - gadget. +### Build container-chain nodes (full nodes only, not collators) +These nodes will only act as full nodes, but not as collators since these are offered by Tanssi: -After the node has been [built](#build), refer to the embedded documentation to learn more about the -capabilities and configuration parameters that it exposes: - -```shell -./target/release/node-template --help +```bash +# Build the simple-template node +cargo build -p container-chain-simple-node --release ``` -### Runtime - -In Substrate, the terms -"[runtime](https://substrate.dev/docs/en/knowledgebase/getting-started/glossary#runtime)" and -"[state transition function](https://substrate.dev/docs/en/knowledgebase/getting-started/glossary#stf-state-transition-function)" -are analogous - they refer to the core logic of the blockchain that is responsible for validating -blocks and executing the state changes they define. The Substrate project in this repository uses -the [FRAME](https://substrate.dev/docs/en/knowledgebase/runtime/frame) framework to construct a -blockchain runtime. FRAME allows runtime developers to declare domain-specific logic in modules -called "pallets". At the heart of FRAME is a helpful -[macro language](https://substrate.dev/docs/en/knowledgebase/runtime/macros) that makes it easy to -create pallets and flexibly compose them to create blockchains that can address -[a variety of needs](https://www.substrate.io/substrate-users/). - -Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this template and note -the following: - -- This file configures several pallets to include in the runtime. Each pallet configuration is - defined by a code block that begins with `impl $PALLET_NAME::Config for Runtime`. -- The pallets are composed into a single runtime by way of the - [`construct_runtime!`](https://crates.parity.io/frame_support/macro.construct_runtime.html) - macro, which is part of the core - [FRAME Support](https://substrate.dev/docs/en/knowledgebase/runtime/frame#support-library) - library. - -### Pallets - -The runtime in this project is constructed using many FRAME pallets that ship with the -[core Substrate repository](https://github.com/paritytech/substrate/tree/master/frame) and a -template pallet that is [defined in the `pallets`](./pallets/template/src/lib.rs) directory. - -A FRAME pallet is compromised of a number of blockchain primitives: - -- Storage: FRAME defines a rich set of powerful - [storage abstractions](https://substrate.dev/docs/en/knowledgebase/runtime/storage) that makes - it easy to use Substrate's efficient key-value database to manage the evolving state of a - blockchain. -- Dispatchables: FRAME pallets define special types of functions that can be invoked (dispatched) - from outside of the runtime in order to update its state. -- Events: Substrate uses [events](https://substrate.dev/docs/en/knowledgebase/runtime/events) to - notify users of important changes in the runtime. -- Errors: When a dispatchable fails, it returns an error. -- Config: The `Config` configuration interface is used to define the types and parameters upon - which a FRAME pallet depends. - -### Run in Podman -Install [Podman](https://podman.io/docs/installation) - - ```bash -podman build . +# Build the frontier-template node +cargo build -p container-chain-frontier-node --release ``` -This command will firstly compile your code, and then start a local development network. You can -also replace the default command (`cargo build --release && ./target/release/node-template --dev --ws-external`) -by appending your own. A few useful ones are as follow. +## Run with Zombienet directly +You can directly use the zombieTanssi.json file and pass it to zombienet to spawn yourself the network. From the test directory you can do: -# Purge the local dev chain -`cargo build --release && ./target/release/node-template purge-chain --dev` -# Check whether the code is compilable -`cargo check` +```bash +# Generates the latest specs for orchestrator and container-chains +npm run build-spec +# Spawns Tanssi and container-chains with zombienet +/path/to/zombienet spawn -p native ./configs/zombieTanssi.json +``` \ No newline at end of file diff --git a/benchmarking/frame-weight-pallet-template.hbs b/benchmarking/frame-weight-pallet-template.hbs new file mode 100644 index 0000000..e79b4bf --- /dev/null +++ b/benchmarking/frame-weight-pallet-template.hbs @@ -0,0 +1,136 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +{{header}} +//! Autogenerated weights for {{pallet}} +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` +//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` +//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for {{pallet}}. +pub trait WeightInfo { + {{#each benchmarks as |benchmark|}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{c.name}}: u32, {{/each~}} + ) -> Weight; + {{/each}} +} + +/// Weights for {{pallet}} using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +{{#if (eq pallet "frame_system")}} +impl WeightInfo for SubstrateWeight { +{{else}} +impl WeightInfo for SubstrateWeight { +{{/if}} + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} + +// For backwards compatibility and tests +impl WeightInfo for () { + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} diff --git a/benchmarking/frame-weight-runtime-template-xcm.hbs b/benchmarking/frame-weight-runtime-template-xcm.hbs new file mode 100644 index 0000000..86b4350 --- /dev/null +++ b/benchmarking/frame-weight-runtime-template-xcm.hbs @@ -0,0 +1,79 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +{{header}} +//! Autogenerated weights for {{pallet}} +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` +//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` +//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for {{pallet}} using the Substrate node and recommended hardware. +pub struct WeightInfo(PhantomData); +impl WeightInfo { + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + pub(crate) fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} \ No newline at end of file diff --git a/benchmarking/frame-weight-runtime-template.hbs b/benchmarking/frame-weight-runtime-template.hbs new file mode 100644 index 0000000..dbc3f8b --- /dev/null +++ b/benchmarking/frame-weight-runtime-template.hbs @@ -0,0 +1,79 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +{{header}} +//! Autogenerated weights for {{pallet}} +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` +//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` +//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for {{pallet}} using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl {{pallet}}::WeightInfo for SubstrateWeight { + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} \ No newline at end of file diff --git a/chain_spec.json b/chain_spec.json deleted file mode 100644 index 769d1ed..0000000 --- a/chain_spec.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "name": "Local Testnet", - "id": "local_testnet", - "chainType": "Local", - "bootNodes": [ - "/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWAWEuwUiFDT59PhHzsQ7DCA8D7aAmGiojqJe7YpQGTf73" - ], - "telemetryEndpoints": null, - "protocolId": null, - "properties": null, - "codeSubstitutes": {}, - "genesis": { - "runtime": { - "system": { - "code": "0x52bc537646db8e0528b52ffd0058e48003ee5a842c0e5110302c2b1dfddb7ffb6fffddb63ff8c6d156956331bad2dff6d7697d2d4ed7f7eb0741868b3c298ec54918011769e3d7f468391ac0003fd0cec20f011400ff661f0124d7418e67ffde7bef2da59432c914590ec80c270d0da0d79b9f1086f3d23bbe9aa669b6356d4a4dabae1ee9160b202957ba20404f0a96b70bc906c8f7a17e8d0a78e9d25bfd1a875e7a005eba67e7e54b1e921ff4b4e99e6751d47c4ba8e37954011a62eabcb0c21b6c6480b16bf38808145f7c51a20731322760ce43a3d14fefa40c42db25a9fb0702f5aee38f832b8efed7b51647a9f9d2376ff5abb39de53871e47c73ce723ebdb3a1fc8e0bb95ddc2ecfe5c701009eb6cb1347cf372f949acf79279be5ecb1217f17f273a1ab5fcc45a3d04b9f2ed42f298ed7ab4fefa69437e4df420d02d2b550aabe6c75edf1c78175298eda4be7707e6c7fa677f4e3fbe3b56ba85dd3afb76b3a7f41ae98d44993436e573b0d47f950322b2b2bebc7d8b3db70949acf2e45f90561b18aa0d7ea1733bf50bffca7fb60203e0e3d272b1786027946e5ca4bd93b0f8d52f6eddd118747c0a6e9fca394f5f36c0a1c2f5f7a7f44a4ec591ca5ec0b429dc326f4d96938047cf6198eb19fce0c64a41e34847b2a42f93c3bbb79a31f39ebc7e9cddfb5c7610c942e3f0cbbfca01c0afc290a499911ddf419c240e9340260ab5d720539df51c07ccb8781d27986f23daabb8ebdd8a480dbc5150079b602d6d7e719d59de79f3792521e9a21e83200dbe5d3490780fc4920f6948a3129373022024a6faee6646e6f8fbd3f0f62dfc422d4d4b38fed92456e1783120dd59be719d5991f7d9e3a029e6754569e769da49f07b1e7768df4632fbbf905a9edd9ea21fd38906f3da9b57e0509916fdd8ada4bafa17c1ea26213d85cba0c7c9a5a863d58a464211d3bc07bfa9d4f8bd369b5edfd0a12d23e7d5a974efb35566f9a4eddba75ad5f6dbd4feaa48e4907809d0f15f5202645ad5dd4a9773e0b00a7b7434d3d75ea3208947ce920e8b8a7cfecb0e705904ebad471bf78a6029d9fceee85dd0860f73c5381eaa5f3d77177dc01d8eefd9a3e29d8afe94350eafc74af5ff3a7fb51c346ef47eda74fa73c34a33afe4e4a1fbd6f293b19f6ec0332ca9f6e0090032a6a5d7b32a4ef31902869e7a1ee37d9e133ff6b623d39dfc4eb61205152f6501078e9edfc814065f21b02ca5eba3744ca5e7acb7ed3f16bfd1adba773bbf4717a37430bc0f609427bd314b576b16fd0f1108f5a13b5b3774c00d0ba7c0c803c8322e67906a5ea479f6f5fc0f3ec0916df5f9097e6edb45fd6dbb5aebf209ab5de494f7e273feb9d0da5245410da2eea43c0b72ebf0ef8adf31784db357a2fab38c6be6a9a274d2d1ce98fd4a5e65fe72f48f5a4ee3afea6383af9e95a38fa6b6ec391db85c26b3e72bbc625aff9689ddb3596f09a27cd195e11cac94fb721bf168e424fbd93de0df96dc8cf1f08f4670da1e64fa721b74b3a78e5acf2440d05409e3d29f3fcae7e8d1e50bf46d7b7b7fb78c2e5db5bfdea6fe73e92b0516883353a70117f527bfd626fe7a1169b8c9e2df8d2ed73ce29db937a76bbeceee93da574eda51844443a753945da2e1647f93da5bceab8039067b2370ff68b5deb17cf6458cfeef54b1e316c6c3d3b0fb19410e8780020bb74de3aee9764f9ec9d0ff78b9f9dfbd53e6acf4e3b49c1066bb670117f27f96befda9b4e41cf0b20f5e9d2e717448a2c7f3678024bbbf8db67c8ed9ade4d4f86f2fb6be78f8adcaea49e9ed4f2f320f6edfc05894929d5e87868f49fde7101c08e090072bb46f0a74be72f8874f909414126933d79e95d7bdc2fe9ecb45fedecfc69ed9a9ed432f4da357d081c5b3fbd9dfaa8fdf4aed52f6e17fb74a07ec99f2ed42fe9d33be901f58b7f3a7fd2933aa969d8a4826712d3be5dfb168388b4536fa7a2947c29d221d25b1cdba5277552f3d0149bfcd982584b1945c7f7c76b17b76b047ae9f57926937dab5dd259a8eb987f3678c2aa5dfcdc2e76fe82d076b127350b75dc5d0440e93c93553d3b7f1dbb40e9ecbc7d7980789186eba9ea79d223a547851e281f9ccf8caf8c8fce47c627c637a747f68df175f185f1b9f1cdf1c9c16171697c6a70677c763e3a3e2ebe2f3a1d3a309d0fdd97ce4bd743d7a52bd30dd191e9c674623a2e1d0fdd0e1d10dd0f9d998e886e4b9743a7a5c3a1cbd2ddd0d9d0d5d061e968e866e864e858e8aa7456ba291d95ee09574727eba070595c1bdc1a9c1d8e0d4e0d0e0eee0d0e0c0e0dee094e0d77c535c139c12dc111c10dc1c9c055e154d8e8d8f26855db1bd785ad8dedcee6869dc2a2a160d412542db835340e9d43a3b857685868575836d40bad0c8d0ccd0c6e8e46479ba37171a9b42db4389a161c14b50a9543a5e0a2a868d834b58e76840646ebe18bc376f1e1f9d2b8516ed59d7257d0a65c3557cac442adb858b6ca6d817b4142a96e3832ea1b2bc77aa1d550a7a85454361a161bc74a61adb058324acf14a9463e21d9c829aa1a970529b36336362c945a46a553c7a05768573833ea1d8a0517a7b251e5a025a8507c6f6c76ac0a9b1adb1a974a5da2a2a9546a55a5aa50ea1bd50c25f984c64061b853d825363828d68687b2e1ce7054b82aac16f64d55824ea119a12551a75429d3855935a74c2a73867a06a5e23ec1193165d0a46851aa1b178a1b0517867d72d75c29383ab50dbbc5c786e54283a1aed151752a6837dcab0a470f95f6826643c552bb5424a80f75882aa3752c1474067a86f260cf6c757058be36e81295065a845ce1b2a949543bf54995428ea134703058236a0f74079a45db5432f68a9ea149417fa83550196a14db135f9e8aa71e618ba05550232a974a06a7c475a296a15fb6351c1e2e092e0e4d060bc746f1ad51e3e08ad062e0bea068706570541c12f5aa5a914670686a1ab20a8925db482abe332412d209b984544226218f9068b823b8992c429ee9a122cd4821241919841c02041ac8000436485af2225c600241843c4083060049401103908e5e3f30b8c04801a2578080038445e60a9d7c96158dade712932320505a7264a90425a54ece244f4b4a4a5c51d16dc22f13ae9be600774680bb041a264b98429042b2342a892ae2e32ae1ba49185d2430b847145d231670cf88408ac9e9c853d492140a41969a304931315152e4c72da200d74c022e1146b70c02ee10175c32243c3125793a4188d4d39209525147962c6d80c11dc3514aa24e1895981c514ab2248564c969894901ae982529254d12708530ba4170088e442541c00d43c21353d3112926175c30499e96949228618a5a5aa284c1058297903c2d25616ab2a4c4246a8984a72527a7232520615af24f4a98445872621a41495491a2fb83d1f5c1c9e9081227a72311f8528214094f4b4e5e786969042525524a522590c0b41402a2db033371623a22b594446969690403dc2eac648427262511442d9d00a5c4b4e4a59e964c10c191227eb9f0129227254f4a47949896fc1292272513907a3a410912242f80cb038fa04449d412a5254c2530895a7a0a811225112c3519e0eec049969e486062f274821212254d984e805a52928a52b264294a498ac904109cc004e9eac0499894a49698449d806482bb858425499e8e447130809b4312254896925470b53013112c35293161228225224f4a98983059b240787160264b4c524c514a4a903ae1088f9b859f9430491d893a218992941227264a4c4bfe48d409dfe3dec0215052a2c4b4e4a3969aa29460e089e948094846180184f0dac04b52479230391d69a2b474c2d209140821c91209241c090101b70666d28464a90424589889d4d352d452094f4c7e6978621a21e8cec04898983c314549a1b0a42402a7254b8e28452d2571623a01d6844489134ce8cac04c28b857f88892d08d814d503a227524c911a9a50f7e2e0c2360723ac224c5f4c4d4e3bec04d48943881406949ea04244b282cedb8561ca524021ecb2527101c917a3a61042529244b514c499e9894942660c00e16ae18356bc03cc73a9ec9642f6373023ba3cc287289bc94822b24742f0542f7de7b6f4f666a295366669f6e9fa64cadc6759db47eddb672b429a5f4526a67b3ed289d74f2e4f9a294ce39bbc75ae6785ae6c994e368c8319d4c27dbbeb4a7653a27a573524a298f8e521e3d2ae8280f1eb4470f1e92ca8e522a29ed64f7e3fd48da514a3b4a3b29259594d2966c2533ed49993267402b536e114023007374a8bb393abf1d1cc7b1656e6bbbbbe9e4cdb2e5b6947e7c27ddf1d11f2008b26de666ee616696ccd5d2daddcd3dbb6d534a9bd9325366b61fa52c2d5b3ae9ec16d0755d17006a9b2706417382d93e2d081a9a3d99995e602436650b28eda63f3fdd41a2027a36734fd8237b989952e69e092865ca94d23929a534019c733365e66e4a2da5ccdd3d674f8ea965e639299d6c99520a2833b59652dadd4de964a6b4bba82768cacd4ca7b5d68705e2b8eeb697bb1468070280ecbd3bac3d0091ad40debbc35ece5a57775b6b5bca66cad6d26b2db3ad3de7377b4ecb6d2fa5b4d2d996d9d22fcf57c7dcc12d822825262c9960a50903e0a5a81194a294481d41a2c4c9e98810065104d8188325264c96a29698a0048952d45213254896a2948e3049826449e982a65706182c41b2c4e468002330452d35b1a0c9d3d214005f2075246ae969890425065c904106182459525222b54404514808a0392d3559725242e1880915f420418913092428719ae0c74b0028280981d4009a8e2041b2d4f4ca0003f730b8808427a612981c29c1974c60927a3a214a441a41ea04012c95a024f5c414b544090913261384029064296a29499412a55706182861b0d474c409044a4b4c515207883a22c5c4002093097e1803254aa22868427204044e4e4aa20e6080234b51148860a949490a85a5a70100406a8984093c26524c98505812aa7cc1931226292447983c1d899252926232c10425ae02300628308db014b5446c451d59724212d4e46969827c81d293122626514b4d94923c2d2d59724469898423514aa4982849a1b024d4e4696956b08169be463836c519e1d8549d8ad5a99836e5139b9aa246784ac6a6a6a6912939c53346a762316b8463531c8b4dcda9581be1a9a9a9a95835c253b1d8d454ec1a89c9d8541b999253721ae12969846353536d2426a7a6ac91989c9246782a568d4cc5241b89c9581b6136c2b158c7a8118ec562d3484cc6a4118e492347d34a32b33481c9110e6395b24aebd33a4b97b2cff287fa0525b3a2d8a52c11a56fb7ed2cbd45661f324893bd9efc84cce992046fdd490a294c6953dd3e791335719cce3e5a21d627112b06111953f876e9eddeaf0ea777b2a51589486f07ead7f5ea4237e4d90a72c0d6ac85d9dbb03ed82fb65ee9a8a38ee72f8b7db0db50663d7f32f6d1b2272fbda79ded4be7a306cf3efe397bab5f9cb303f5ab7376211f72f5cb7aecb9f0f31bc6beeb3aaef3a4f6c2a0eb9b7f629e3ae71d5b60cb53dfbce3282620cf5a903d47813c63c1ce53ff5ab07aea9d779c6743ea5d17939e6f5b583be9d50b35bff30919c65fe74d414434afae695a8ff3f701a76d3d5a77bddb38e7df3851c8f5cd37cf6f28df7e42b8ea9b38cad7384efb40a0ce894dd547ea4c626ffd7e203459a7ae89a3754fdc6ac8567ec75de7f1e789a393f7dc86237deb3ee1ec28cc17c0a87c3c0af305d6252c697a2127423979ea369cee1332aceb3c9f9061dcfc31f63724b2894d9f53ff9cbf203d57c0cfa72735fd8850b73ebf219d5b71fcbc13833ad7bc7a7f433e31a813895cb77edd3ae7dad59aee07f36a43a86fe268bdf3ae0b47cf8336e7efdc86419b28a47ae79dc8859e8ff2bdceb726ce3b71943f5af79c3f103a91b968ec7cfa2672d1b8f904817a278e9c5bf7c2ce93a67c6e0be54b2bbf7dfcf13c6aa079b201fde4d97b8c90fa35dd937a4ac6b8f6aa662c58b1d2a0019e36739175ea32bc6ee72744fa1547d7828848bf2efd8ad2b99722c745d3ab1db7cb90b6ab6dbbfd40e09e9d6320a3a41f089dc871514bd9db70d4be9db9a8fbc957116c97013a4db7044ae73a3ea11dcb4ba7a1f85576b23f0f3cef50ba17363507edd2a5f313c8332b5f9e733ccfbde75d373df971a08d9b3775ce71dede75183484ca64ff39fdf9fc7987fd9f27757b94b9c8c7ab77d8e33ee1d8f413d2de238ef2dba7f727a41df4a409864144da7bbcbd470c838870dfce7d8b1470d1bce03c62d8f40e5bed9aeee33df703817bcf296020d3eb074210174df7440a7abc2794b2e78f0329f3f2e3490dcea31f9136d1f4e9238eda5b6f6ae77cf4a4f620b604c7257547bf0e925c7e42a4f737a49d6beae1a2e93e5c34654fb9684a00a4b227ff79b7759fe8e3cc459b8ff76ce138bff39eae67ebfc0ba37a7c3c4f6a1fcf7bbacff3adf344661f9db839e72d9d3b64fb7345b05d5614bf8aad9ebe35741d4ae9f2ad0ceebd1c72e0de7bd1e8388c52a5935fdff956bbdaf9e3a2daa1d6aeeab23a871c00e9d7ea1f0309d20c840c1f1f9f1effdcf3ce370072beb9e6d77d7c7c7c7ce074ec3cd4c934d410e222fe42c9013f94ccfab8e8861e175d97e10640eb75d4fe5ef71848fbf58f81f05fef70e85ef1e009ba21d7dba3e1f5eb9b6f3ec69e7ad2dcacd6ddb0897c1a36917f6978bdeb76fe1b36691f689b38c67ed33c69d2b0d3a494d43529a5748db66b52cad7342460bb6ca73e611075e9d43b0ca24ec30e929ed90795b4fd5eff8440c9ac28296fc9102312601f42aa5356239369de44fd7a477d6e621414db91c95e6ac76b3ea794f71a084bb8300a0a02df5c445dca3ee76314e752f635e78f88d26f5ec16fce85501078c945946e22b30fda9f901ac48a63ecadd350d342e9a37cf61a8eed34481415b5dbc6acf29bdec94fd650aa81755313c7d86b72d4e6bdd7afcf16a19cb0cf907fca106abe94d7a184ae775abbb4cd6f38deed5e91dba589a3543f94ccf3d4ede6331ca73701df89ca9efca839d4cdf324eb9b68afb90cc7da44bdbdf32b6aa1777dd4b4eba3e6501a1594271fd5c4bef426f799c4fe7ad2bca1752de4e77084ba76bebaf5a4d6a4faa96f21d47c2a060d91b320ed3694e375eb1d36694d487f75cdf9130235e50dfdb2cbb0a99da578c3d9a37ccd357194ce7e7dc8fc9149ec8530d41c429f8a4d536c626f764d5a34ba0880d27d8658b76ee5c6ce9bf3e69c3387ad76b17321184aef6cd844fe28dfba0c9b48eb5db3e4a63d52742ee226eb1dd8afeb1e27bfe32f08e746bfb98cbd6cb2de71ce40e47ba17391d1b3df106c176b7f8958ef449fcde59744731681dac5be89ad503e5bdf38718c3de7321ca5f4cd93a60c47e99c771db769da4bdfc26bd9477ba5eca37d73fe84d0975b389b7db48f12882df1927db44b20b6c433fb68fe0d6a6ed3b71009381dfce949dd21db70ac2e5d0b83a40825ff8a426a64b247c077d2d3c2a267b7b12bad115db57aea353c02365537c08f959f7f89814419bd17f6188fe1d299452b5245298259afe201b3d2afd653a7a047067074faea463fb67e643128c0302aebd24abba87f001ca595a73eb2184fdd093b3889dcc852440e4588912393c1ac53980c651488167880b2831230aa284530e9553c60ec54511a76bdea0130e936ac7a00cc499810a43b293289a84baca76edd499810ae5bd149d224a27e45276142b02e45273e89a85bd168e4d94cfac858fc861428bca0b1c50f5f58e14fbd070690613119d828a1454b9a3764da80318c7d340ac1653298b4d22f033c7569f56cc154c7baa996cec824e249c4ce3dac54bdd0f3ac4a9d378a753eed4338ea944efd7439e7a958474bfd8a4f561cad3bcd3176be8a4f3e52976e7d86d4f90381a9b07a2b7ad1aa8f533fd670acee14cbf2f789a9b0faeae308de5ef189e77cb54f0ea09a7d15b1bea051801b26c411859f2ed969389dbf0d7871c23ee59395959505b641cdacacf72213000b5dae6c8922c01621ae64524b78ce7b9129c00d13b2260ea09aa120632aac60ece308be42e139b04d94f11c18fbe8e4ab384a9fac80916754b8bc947c2f3200c419a3421932198c4527ec534f5a74d0e2491e990ca689b291bea8382ef919c2cf17717aa8834a2683b127f5073c7e8a3cd5b1da36888edff856bb62526630290dab4b796bbdb5562af44937e817a4abd3205032eba95b5188d2503eedce825d6320d5d9bb3ae5d47c5a9fd6e7f5e99a9d3eadcf39bb1b0ab5cb361bb4866cad4beb41d4e5f707d135acd72b0ab5cb8ab45d1a17b17c1ab6151d57a70cc43a7b652032fcd1aeea75dea85dd5f91b85befae42f03607bf584fa88f3ea1a3010e722cdabfbe817d82e6df3ea5cc84da4f9501f694da48d415e1fb59a48f3ea7cf5e38faf1ec440e457df42a176693ecbb0a16b3ebdcc03b54bf376e9f2d33ce8fafcfea02b42c9acbfceb329739e6753c8f856bb34b750f43751a85d9a78c3ca45ecfc512e624f6a1bca9f92b1da334cc7f647ba10eea53f7f417af2ec962074b6935f10ce3da77ec3cf9981745ebdeb4221b1e74fc86dbaceee85e375e99d7c9e4d91f3948b3e2161fb175edf42f9ed9a77d3e350f32fd47c0bf9257b526be1289fc55173cfbbf6b4b0591cbd6bcf0bb977ae9b9df3079fb3386a8f02159427b28ff23c118a49ec371f4330071d59cf84b1be5ca1f29be88923771cf27f61573d2decce69673d2d6ce78f03cd7b7e443e7144e13b674f1ce57bce5e3f21dc33178d9e739e34bd90bf897376f909e19c4528273fcee79cbf21da776293e6ed49cd85a3a63994ccfacd9366526ffc2187b14e52243aee00a43e027d05bd5fd57ff46b82af0e34e5cc5717ea577fa5c0e82bd05707fbd55ebdb2d7ea3c54c5269e517511009947c7f6c76b176d790745a65ca1415850af7eabffadde01681d28a95fa3d641bf46d048bfae6bd0af09fe7a06e4fcf50ef46bfee5377f2940faebdc2f0a7e6436dcaeea492dd47578bfc81efef60371f9eb1db7e4d90325b39ec17e4dbfdeea17f5eb5abfa45fbfce43576c623bf34bd5951e817eb26b94833967fb742106c23f5d000c4448c79dc1741e1a79f6ed1d6d5711aa155288b30296d50a6be2cc81f1245ae1ab2013031b3a71c09106c693a89de90f153be9b5cb257dd49ee71137d108bef4117c296c9e7e1100ab5357bb51bf460da95f2328e5cbb7b7daf5adc1848ddecbd0e3fb6345af5d55f49e6752664fc556bb24cf9e2d8855d973744b206d1708fe9c3efa35c14f07b3fcf4d674cde340d7046c219e67357cf19dc3f38c0626def53c9b810de99d0c8748a960e7c75812f6c7135e78e2d97d9a809389e7590c625eaa799ebd50e77d3ccf5eb8223f0eb829e43c949167e780b3a3e5a1a67a7e9402230e500e3f4a7dc9a103e347a92a60d41f99684104028ec0fa91c9192cfe86683219950de1ac670fe5773efedc315c7d75f3d23b1f28f9d43b1a56eff8e3808aa3941f91e92d8e5aac3cf5f6fe8450979e343be4afa10c838854374fbdbaf99149eca95787e2acacac8e757b52572b16007996e5cc871f7ed75e0dab9befb082971f07feed346cd2438a524dc4bf7d864d1d8ef2abf7c781cc7a289955a7183444fa740ea44f51aae3190e552fd55fc5b1bd89fce9b19fe1cf30fc0ef96528d53d7bacca86a30322655d7b1c036920dcb3333780a502a8fb3010a6e3c84aa2e9ec2c9d8aa3f6a34dca72878ba87ff4b9014959d86005f053716c719cfa26fd2444c997beb538b0f33c9e4529f3503edf44b3b201cc3ff68ff6a7cbcf03507a273f22607b27a9e85623bf205054266339f44c8563e7b06c80a403e7d95b1e318c9d9301a0c652c801c706354aa0c40e3ec4bab040e54c961416d668c20b14cd0b158a0010a80bd8024320059a3652b29841b3850679a6ece0c64d0b56acd8a96e7c0740ea02c678e89205186fb6e0e0441b30701e59c08c2fd6ac10c40b36b800bbce73fe3aedc18cbf7fddfb45f4d75dd3481c9d90c02e1ae8c305d402853aa90310c8bde7991039fc08f42368cbc0aa0028888f92b46d6601d01090900309f910900b0931c39406722021a079943485a85225a9039e6d7004a48942ac100508e887910b01cda3211f1d00b92b083c3f1abdd4bac2c147cc44238f977ce4b10f1f1935917424f65103a300786a9e1d887ad281003db05ff6ba74a0f18a4e438fe436082efe4281dd7befbdf7de6b6f106ffeaa00bbf75e6bb52a3d2009063442d260d25b421f80e759105f3c7d9e05d1c58f432f6fd3373a8d813aee57e7d3514a3b4a2d6739cb598ea3dca5d2a78fb6a75befb639bb47f99c73e2387dba13eb733a93d8b76b4ded9393344a37fb476b17bbf47188fe80ed0ab2ce3322b23cf5202b0a9999b1f2d591fa68ce6432d913587b920719480267e43a544645f559c96432192c0ccf7ee0d27ea77ffec874bed9ce066bb8841b38f125dcc00930af1e398b00431bacf91232bb248b5eae50bbda81e691d644ed57e42602fa6e57ff6ce0c41b1fb56fee1710dd08607d9ef9301bf9cd5b662f426b8864f4a35db2872e3e7ca9326a17d28f7649a379e43591943e70a1f27a00d9b3a12b1c9aa3f6163ccf7c78f2e5ce8f4364369755fd65cefb976ff7b2e5fb7be8db3b1f4f1201fee8d7f576240d80847ca8ab61cbf301e49c7df3fa0da9ce8963e5661375e94dd3adf71744dbbcab5f106da3de49feb66e7e4334dfc4b169bad634dddb42cde5375d0b9ba8d376691e7f34d4629ba444742100250aa00c94ee2d0a852c25033af6d003608c820e803cf341cc5327eef109c909c9cb1352932a55bf685a93aa7514f46099e1d3d0374df7da355ed7bca98f4e43afb9477f00aff38fb4d2a77d4461ed9e064213f952149f86de8a4dd4abe8b54b08a8c9aed2520aa9ea32994caace39a7f4d9f30d172e48dafc213bb8401103129caf72f0d8f912ee53ef26f79b5ac87ca2a38c25a594d2e88bb4abbe91524a362cebcce940f76c6a51f3da357bc0baf71afd752424a4a1911ef1f05103fd8f5a1ab4ab5be18f266ae711f224e27c47c8f94fc899013bdf1c0cbd2bafbcde73e5f5efba775dde9ef00b3d318a76a3ff10d64f531e3a16b17873b5fb09099f98ce375dd79ce9fca5de5d71643af2e708f6f8d87aa0df310e79a972580f1f3f47a3464167efb8d04b0fffe1117266c08d0b80b71bcd2300783b388f5a3fcec3dbbd5fdeaeebed3b42ca2314e242a4765d0f40e80280d18e909f7319cda3d68fb7bbe6510fee27046ad775304c6ad7759f7083765def094368d7f52fcc40bbae7b6107da75bd0b8d70d1f51ea1d644d75b213711cfc6faebd2e527a49fd334f18ab68674368dede80ac70306e2848b245f8d3fa8061d68228984e74e1d3b2580e95a7de4a389a4b3bc2faffb0947ef7f7e78f848ffe7f31fe711d276edf0d9f5740e76eed3798fcb2fc80e31697621f59f9073301cc107fa1e318afef42ff442ca75a293f7daa65df749c3eceff8e328849e4fdb3ca40e9ae8ba9176dd24edba75fe3aa703c858bef408b95d3d5c5ee192e7af152b99456eb93d8ccca3a126ba7e8746b0871c2a5854ebdde2bd5ce7a3693d001080a1bf01f8014028adb4ab87df168f1d3d3897618fb778ecf809470df4f99cebf9b49ecfebb84d5a014759f5d747befaeb124bbfee2875aba415ab0bbbee03e90efd75ed5e39d35ae002801e633d379318c8122e92de7a1ebde72964f43fceee7d044ea2e9fcf500f9e87e42f97d798674af381d40a49f90bf9b3df49cf74744e8cc1a61bc79096251640be5fbb87c1271e217caef71239f473b7cba6bee08b54924b9ef781e2135d174b905dca05f9e4fefc0837e757e379feef241e739bf3edda85fddf9740d3ca49f9d379d909ef3f911d9c1050a183a4ed279e99a9c392f3f2b76a05dd229fbf4add23101c076fa62de3a50bf3ef77c147a1efd791c7a1e5dcf471b34917566366f9d1302ac45426897f5a4e6c48023c379eb9c0e203b0a5558ba0ee5b7426e57cb25d5957e751d273269455af5abddb65af4428bcc236f22eb9a06ce232a72138d40551c815ae2e8d665c8990147edad8ffe76ebf11647a46f198ee0b7bc878f14003e6a2fcd803e00007b844eda651d857eb5dcd616c8a377b4ff746b73197ece23dcf103329ed1bf271c9d90defb4429fa1e7dfe86b8a87d0b81dad5ae859c19b05d560147c6f3d6c72aebd62555bf4627c6f39a75149cf48bcafa55bd8f349875d78f3e62d8e840fdaa6e5d9b47d3ad83f388ba751eb2629314f36c41ec475e1cbc67272f0ab70194de55f0b4cc0cb2f717b67ec77bad5feb4dd756b6d6dad15a6b297843cdc8c7b3377391f5a4c95f907be5b5a2fb48fddefa838bd8adf56baba4a2b5a1865a2bd731ad9d3f7cb48b3dd000699b257111b72aadb5821d18c232f4d5e5ed62f734ee00890627a4af4ca95b1f401a0b9de71979e93348971a88c192018b4e1f23294342d4c74b77c592603557580d05eaba0e6000a713d2533a7bfa9b1eed2a67b7ebb5fa354e97d3a98fd23d19b6a64f6f92620b1cbd1f81a6485bda8fb3d5ae06dbd52dfa74fa48bd76f58f5e52bba47b7d2461ed2d500723d77d697f9c73ce0984bae4a24a9db4004e4abd17f571ce39279de2ac5ebfbc1d187ac1ca4ef38cb8557deaf70b429dce3959eba1e2b8c40550fa94a24f87aa4f71d47e5eebe3bdf74e6fb773b6955d125c99ab73f526aed569ad6ee4abd3505a81d2a927cd1a4ad5af5abbe89c683a1e1a414bd9ab4b66a6eda22c04d47aae2da7e8d51d3afaedb4a9c6541babdb2aa9375529a5f70cabec2b1df38bd2d90de896df14251f88d6af50aaf9d53eb253d7fa4543a6cfdfc42d8e0c64fec84e450eb567e7960d91b3ef29a7944e6af0c3871ad93d27a5b55aeb75f805e934ef7eb2564dd3ae56ddf25b8af472347a29f4524a4945236671f4a1676629a0f77655d7576baf38c61ee9d91905d6bcb1f5dc2e7b65919c267f1538525be96c797b46145ec6004a678f5dc9cde4f02065528685f5a3b4cac12a0f96933186a841d6e4ca2941955857ee00b67723e369c6f3b48623abf99e174056f3a3acfa2ca0e6a3acfae63bbf7151f447b0bb55aba213ab9972410a1378bed297213fcfa3cea9b77b5dc84dc47824d6d6eda66b52bfc9fa75f6cf3d6feb3a71d4baaeebbaae0b47a3d7dab5699be75e93fa4dd5a927356b5430e9df69e2e8dff90d47ff2bd4afeada246af7bcddebd7e70dfa08f4777339051c7d7cfba6798fb7ff70427a1fd7bc5d837e71deee1382edea5c0bb57675de137aedea1c0c819aa8f32fa4edea3a975f904ed43c696a3f6adf9a15998a5e4867344ce794e5706e3767d79c2c7929a3a7fe84f49dd370447acac1742a8e484f3797e138f4b273ee23a449d4ae69e0241a81807e44fa76a905f4bc7d447a1f6f4f6b814240434f7df4af3d3f9efad744d4ab8fc8eda29cf784a3ebab7fe168f4558ca2dfde8563075f7d7e4138b1dbc076c94eec8f8893336b84f1e5ab752ea4bf85fcd369f884f4556ca253f4da255dceb305b11e49df749de8f24135692f90e67ddee7ddce939a9b5e7fd4ab384ad5d7dc6ae2d8aff9e6351c0a526b685d839afa9ad41a94fc4ac5a65472ed612ea22ee460bba8383a21fd743983b40cf993158a8ec5117cf6bcda906301946ebdbdc98a5abbdaa96c016cf7445a4729fad2b9892ea27409a4b0a1f23dcfba2c31ef5c1f3598c96432196c64368c859c481bf4910663eff80390675cb0f89fe7190f613c1154e814c176d5b055c52f2ccc2c56faeda801591fc16ff124922e63987244619456672b8ef2abd6aeea52f23c029b48baf4a7b32e554f9df69194c964b22a30eade5307ea23865171741a7aea60bbaa384e975420837d4461d2f986ad2692ce950127789e7151e3bbe71997393f82b62b1ca902f52ad42fa7a19ff487089d5963ce4ba7a1d3107de9944a59828efbd52ff639bd9395ce2fc8ac21b76b86d39ba16cd951be0bc460ed294c5f618a5220006b6f71aa65057c01a506f8b18bd5918ae3d4531f370ec339d4bc862228c04d9a2c183b0deb1c6307a68932a6c28afab8fd34003b7f1b308091b4e2e8c80a754a4376f91109b2ce54551a4cf3cd6f58a5c1ae73384e85d03298e65b180509769bd0af1e34446ef95ac7d80759b1c875fe10409866ad08e5f355f4a2c57e7ad294b2d76ec89368d47ec66a770b475968584561d467180208a3ce3ca708e5f35e68b7c4a186393f38b2a163fe40ca7e8aa3943d7f20804f7d864de8d3e9ccd3d90d3317b1cb1bba8e89fcdec426e99b5bddf9b1a324175597d21e8ab3be73fe8478ce71ce5f07fc9d7802d04b01bd14b9a4d9a3f59a951545ddf317a87e7a1d32e75bf57a2342dd73f911f17cf38e7af2e3e07ae732ec7c0b3da71f11cda54b91b9488e2d6ede85636f4db4879a75c84629f9edd2635676191d3f81d4a54f971500a923e0ad9278eb53f33260a4053af4ab9ca04806acaae60470a6ab5f41d68dbe7a917e8d4319e8d7e8325242bf4620bef2a05f92672e5c7d15c08fde8f7c25f423c39933c18f5ce7a8c202930a9631ecd0e984cc892a27ac9cc0b2038d3a820b1a3b48f1831c6c9c41a20c14668431e60636de006d55c75018c940840d6ee0803233138486295358d932658522dcd822b76b5f598400038d33d24081b22531471930528c71860e10331ce08e4d83830f1138ec80840b2ab800c70c6b66b09a752983e74c193b6dfcd01687ca78acba00834e15317071c70817c8940962093a54d842cc97b768b0f1435196eede7b2f6d63c6dfeb3559e1451d6154b862881b78ac99cde6e410c70a2ac06e9bd9dfdb86cddf35eebdb32f1f161110702b45cea4d879f2c6126ccc4035e70cc7446b0240c0a9b3060b5aac3863e7f3e9c28b1599384a5c51e15eb4409104143364757932a4c66dd51e7cb65b4587cdb402c5bd924d22d6aea04fe46cdbb66d5b1e2a9bc78acd73c5e6c962f370b179c0d83c656c9e24ac65ca62862dda98c3256b8599678d9f38594c11e3839c26baa46185d69233abb55639cf332ee07cad723ccd8b2882e6a871e0f4b8d0aaaa02bf29b31d55ac2c9be9a599d6da2f2dc67a663c37364d0c763c36b20722367bc5c6a6efbdf75e2bb74c22d6aeaa2cff00e68732e017ace9fd90c45fa7debd3788cac572e8210c2a555c8899a18a58191facc880b2850e58dc80c94200346a1c71c4c1b5acb5d6a6b1a2f3d63eb121062a6a787325058d9cb72f84f116ca145a5cf1859a2d3fc0ea808d06b550ee34113f140da45c260a3353b27870628d2e6023d0cfd951e50d256e38038836ac5a80630ad83d635ad0aeb96cce4451850c0633566ab00c71041a432081250d20664166f8a880556badb5562aac3c0dc8083543806193459d276023d0b721a28d115fb77043078a2ea8b8790356cbb0d65a5bdb08e1a5a9128513758c90638d217e78a295b9322333074b1969fcf0049daa5d79c69c7b469d7bc69d7b469e5b87ead6b172af51f5c2d3344dd3dc68dc03661dfb43cbe0668f151a8878c2460b727c89da014e0b3ed871230b9b2e221957508fcd8f56020a821660c8bc50c9228923b25041870c70c4a1a28c1049d419a3d65aa7b8f2b45a6bd5c05a6b2d15747aa878e3db827b02024c982b31b8a0517544e587306ae0c410860d438c21c6176bad1d23cb3606976db34e6db7613d36f3de7bad26115b3679da4b5345cebd60d572afc512e38c3b334381cc4007104358f9c0c5cd075cd2704305863536ccf18236676bbdb1d6da23ce3411cfaeb0dec8a94947d0a975d2999ca695e0e78db6c2ccce994c824e127692c0a3844c892a25ac94c0a2c49626e21f916668bc4023c6d74a546bad400d9e2ef624892cae6093c6156c3a30638a174d54b143192c65e2bc1186c601188e08c38a1dbab87046842794c09aa30c0e65d61853471b606c13589365d543d60f5abac0b09650e28a2674006206326bb8f18547854e8bb1421873648991c20c1b38714479218a1877a882a0220d39b3672d6b7fd28c01b5edde7bd5b0d344ac061e0d6c81f7de7bef15b4e5809be78c9603bc0ae11989f3260cab299ec4b104cbb1029734603801278e2b610cc08b9eaf9a738a2f77dea6f13972831a66c0307125660987a24d1e2d4858b15227cc1aa138760dd51a2b6baeacc972d5c3bdf7de7b25c65e9999571831878c2ee88a8559022a6712b1a6516bed6da306eb0835aa6c49232786e58d37649882061a38548c91a18c2e241369f490a7872c5ca8430b58c39b38a86e0082083345d0f1160e4ba193a64a1dde75e38a2f71f244896187180d6e84b9624b942c375b7c81420517611aa0435b6bdd23532b08e4b304d6826fadb596ea8b46ad652395c092c34a071f6628d3c6013564e9214c14676889610b6badb5d626adb9d268cc4bd61c6b3c01458628b25ccd7065c35bebc31c315e9882041354b4e051c54de9a26c54763ec6827245070d258a10838e2bd89813470c2090c0ba63e6093c6f7ef89122a6b7a81e9bc9034e0d5bb688a1c6d51a1f8081c10d1174de18a1469830ce78eb06086ee2ced6449e4d0d951a2b40ae517b65f5d6da79455a16232b0d2a54cc61830b6e880cc1c4186260a183e506ac36dd99295fbf04b5d6eaf30d5b3127c98a3a1be869938ab5d6da7a85d576156616d550838480f2060c46943153c5075459c0b145072e6c4003b3697e80a3822cbcf42c1de1a68818b45c09c186065cae8c1127082962bc1481e60d2cbe802e93c9a0c82867b68f3203b9ebf486ed489b33c260b162e68533ecc40a59d068e3852d62c8c06a839ea9e15e118419ab30aabcc1e18920c21dbc98c30d2b325f607911e5af7b56fc9861adb5d69651a689b88c24b432d48065b0012a030b2f038ecb480b290119d688212b4f0d61d4f151f9428b1ba430a2853455e0506badb5d6aff2860c2c969b566a7743196d6ef04215e7899658982b2c2dd49123082b2a3069b06aadb57a5aadb5d65a6b75e3c9ccdc89a20d225890e10c2e8ec002c50821ac7033031158b0d1e6076603cc1675aa6679d89441c18d1a717440b384175cc0ac17326fb1a89081892c862023c6942d585256bcc8428522ac78b8628a3e43bdc83ef39c26073a673ea9b53bc71c583c6481e68d9432acf4b4c1e391200b1530fc40850b6380c1c65a6b6de582e76bebab970606afb556a05a6bad350bac2cde6421270b3ad619f04598275e7053c51461bc00e2abcf572f8ef87af406963060b0f0a4a1034c8a7befbd57a84cd9e18b1bac58c093419929b678020e1e2b6cc43163479c3c0af81ac511d60269d60df7c55b3be6da343db8f1f3d6a6192361b0f3f52160adb5b656e75a9bd0b0dec871a3a13963f64c8c467d6898a521438c1c6e10332bc15b3bfbe26dcf5b2ec11d3967cca0210e2c69c02c14636835e080868e0f4dac11732786060c22d64853031a5aaab8b8f2c15a6b357a850d3392b0da028b0f649000070930aa8cd8818e2dd6c82239d8b09ae10a891b9650230e3b67cab070c60d413ca1a5d15035115bb992850b982434659680f2d65a6bd3e091766432990bb05a6badb5e70590f13ccff0587dad8cc7d3a474a822660b1d5ca0c2021b819e0e281c1d56beba37441e39cc3c09a3e6883a60d589188ce4c864b228482421618d7ee80a1c32e06163461b5ea04982a29955c044181f9e6063660d191cd4900102ea23a37abfd65aeb1a569a88af6c496b64d936a04f6aed5a6bad05e2031e6690f2250b2e5758c4a8b600c2083a54597429c34d6c5259921813a70b2fbcb421878832d86839230c1a31d20021c75bf12d135f90b66ddbb66ddbb66ddb664dc457586fe4d0b1cfc58eb548d65a6b27137eefd5eebdf7de7b6f0dc176512458744943c71b5f68f0a20b38d0b88a3366cd106d54f87b5d6b43e5af7b69c076514aebbd56dcf97b657fc1bff75ed6e8bd2fd4f91bf41706297f81fede7b4110a474e6971a5ae812059431649ca9355079d2658a23b890030b4b87bf336badb55aea3c41c71417ac54c5e0038e0d306801228c32bad870d374d1c107023eb540d236eb2d3b32994c0aa7c1a689180b0d0e3807a88edf6922fed19594469e26e28af449bd859e166a596cdc80b1840c7594c953f3a8f95af340f195fb3a471547e4d1418c27ba803de0de7b6f6c0bd51424ce7872064b565d42081b7c18624c155f9091e70a8a268d96039cd2612a66acc51854499cf1a24d1463daccf0b5e1c17b4b25860a54683063c5181a78adb5d65a6badb5d65a3dad5d735417687dd438f0da45b579d46a22ead4d67a45135feb156dbed65add486cb568cb6716dc46863960c490a54c145938eb8b29a04c31851a4e54d1002d9b5b07a2338ea3a426ae2611ffb685e0cc1860b470d3250f962b23deda2b346f6d1d5fdc60810516c0a83a030332b35a8c8e877a7a508b42faaf5b4762d7bc566fb936039b882b50256adf422dbc769a99f39cc5f31764a8d2966974dccfb4e39e69fcc308694d0f439041041a5040a1c615575ac851034d9c37c0fcb00d314106485746e29439ec0c01830b62c63411434311723041860726ca54f1cad0c0e307d295e61a02c0f38c8815a86ca655967402812d385a6810e84799337ece39e714c71f33d460faf803490329c09ee759992e7e047db4cb7b9e95a9a20c9b1f5d33376cedac4cd5db221f3a6ba9b5d65a7bad83d659b3d6529757203fc561cd1a2c906a5c8580bcf90c28ac5cd9024b8e2d7498aad3051d29f2c451064d1c32c20bb2d86ab0445270de1475876e04d06a68346a7ab9da6943d06bcd580b4f8831841046ccce9cd1c097336ac051270a335ac8b431c9bcd1c3a8d52e79e59ae110b7382607afb160a94debe30cbd7685daaca2f56a15a948a994773a1e1a5b0c386adf67e6d12667f26a7bd35572362bcf4ca2f6cec83ce27c643aaff9c8765e26c55480614626d13ce2bc1d04eab850c26e5864126d2e2d7cf3ed570c12e1bae69a9842e717c6b04e7a5c9019a488b671ae5de7c4a9cd53989269de85dde69c771b27ca6e6f99479ab7f7977974bd7d0bb933a86fe458b1880cfb4bbb2498975e440b8344d07c73ed8641225cdffcfa6634c5223e89bab74ca2fef2ed2de6d982a91aab6de78c913dcf8498f345cf3331567c8b6120201751a74ee53cf5517b4a818669938809706c314f9dbe9947550e3c5b643299ecc9eccc3c92483c79328351a762bebc91f3d4e76c1ea5d02eb1e6515fafed57944d596fe95fe6d1d53c4aa1fa9b79544527614490691011850ca6f9752753d6ab6b579cb26298e9294c555136e19c7145066b17338fb6cc33130b0fb512338ff84ace3c9230ea8d671ebd68b86307467dcaaeb050d96c3af38861b30a6b0bad7ada76a81cd934864e6320b261ec9dcf9796f3072502e7a0f370fe78f0d70aa35a3b7c6b85513c5a528c3c23673b368985a765f2eaa583e29b97d2ce4bdf2187ce4be7b1f9786d7b7a3ca8e5e00ecd3fffc41460d3450812e1f31fff115380d11f3a7fc40c50394c27a8e53d1e83813bc2a81d9a7f6dfdd3fc13837ab80f1ebbe5cbf7108b78a29d97be893289e5a5b7c222552fbd475a45f1f0282811389fbec3a3a044a8bef90e91037e2fd4a332e0e4ba75ce33e0a4eb7884513cfcf3febcb1843c895afe85413bdcf318ace31146f570a828118244a8ce3927a660bd3ac33e17214804ce7ddc474c4173ce19d6e31560988f77f22bd2e3f22bf2398f503e7f457c44ee0c64cf1782413bbc3d066b85636fe149c4c3bb1e61051846559df34e7e2258afde23035a06ae7762574d22e99c18d4f2cd3bb86d8515d4128b50116812499f224fa2188c47b8232c62347912c5e814d81a165820d5b86e90d44113da10473ca594523a8f0060060d113a500181050f1cc7142f2a3c1143958c881cbea7ae8179ea9e029e524a29ad940e51e569cfd3216a784a29a573da6e3dcfc6fcf03ecfb33168ac14094840029460818a2a62b4e0e922011fc841863575ceb49145860b99ab315a3c38b58ab4810d8580fc0b42bdd8807cce4aa74b6a5707535a119c64defc9c64e2fc2c7a0a9b6ee48454afb4faa389a4d3d068ba380e753beda1f62144140f1e01c458764b27653c9208228c195615151dd8083e173dd23ca2b0077041439b342e4801851b183b2f754c00d05f7b3556aebcf4f1528d15aa9772e8a526dd8bcc230ae3ee0dfa351d94c9644f60235f3dbb8c02a4cf3321ccbcf411e859ab4290f95a6bad140a2ac418a07e11fd746dfa07e1c680f6b95ff65b7b10889746790200f2e0a31fcc3f824021b00514c683d109e96dd206fdd25c7a07fdba629a01a86489d8c95345ca71d629442333d2208000331600303020120b87e389aacb3a14800c81c256484212c7024992c3308861188681188610000800801063906114351b56a39db8d48b84ea0a51e2eff5f24c5f814cca09f2626a34629b5a69405ae1d635400f0f433e5eb222bd84a320306b301234c8e1928e057de4197bfaaccc698c4c802b89082bb54d0e9cf6895b479af041110a743351e2d0e7a43c9e494b0b5426bfc9e11aea1d668f56fa323f2870b3b2cda302244bd72127f250f8961a417e955d2cbf28bc990f83e42ad0dbcd02d7357e7f7adcc7c99b1aa7d3db475ceb238946b274fd8e635d5e0913cc6138c76ccc9eca05b3d52e124a9b582b84a2edd34d67fcbee080fc53f1c38ea8f0ab157181da53e1738650e1c957968d87ba1653e8275e3e87f322cb46d282822d0a84001d92fb701658d3d3094809fb06cf77d70eaf05a43db0d6a21e6257a8864236f66219133a2060b113e4ada024e6268edd02f0d1c4a148563193e14a6e10b35791b833af640f29c42a0359344fbd4b29a1401f30f7056564e19259055e22b169d6313994a53aa3936d98641cb9e1febafcdca38b4cbf015f8deb1a37af1b3022df8f399deca292c6da17e3e62037dbd01a4fb3d1dc8ac45013ff9406d852ecbafa5dd9bcdf0d1651b82ef5fdd4f14ae14ca69096eb298ba344f7653319efc644f6d4e24a9f45a33d5ea7c78b25d1a319814bbcb3e836c1f889af830ea1e371fa5ff0cd7a679ec21f2f42461646d4109a4a1a1cc29a38c981442fc71f09ef383fc5c192d9fa710b0e9f2624acd23113772a63eff90a682510f8482b05b3f39a6ba74f05a4058d58213cbeaca549bf114c451fda2c2f96bebdda0afbc11c6f23e368955d7410a1eb558458845d9c796b91d529ca5cdb259e0b5f089f1949a28f882d3390a4d0a43ab67c351b88dccac1aa1799a007b4543acac9cd8c15c295ec0cf1bfcfd70959d0a1c5ab35eaca874ef17f3e5780c077b4dd64dcff79f84a5aeba0927bf12313a1b2d52693fa2a4b9c19e0072455ea30d4a5df65945a9ae5a6c326c17ec0bd9feffcd0e3764281294d633f40ee7980f3259bf1b639109f825c71a2aecb41945757ef7128ce121e8be53cce120b87b64d8c926543d3ea0590f9e814a98814ab7d3fd5a4b0f8ac4b302e67ccb434d11c290c2a8b0f3bebedf3540751531914ace2d28cc32507d2f3e19d9cada05d8778ef38b0af3eebd15f24ebe6dd963d324772f4c8d91345a0f95ceb94fcd5c150160d11fa2d7f065f43fca73ef8d7cb1857c90edcd837d047a75f1c1af5362b463afbb10dde37a745dbb896b5cb53ebb95fa307b82391d073fa3a7f04f354e7a26b2912637a0717b3b8730122b3a5e26e6cc403ba0f48ce36c922a105427f135c7e61f0b2cff73a241ae13d37f560feb35e06a196efa8d09659bf8bdcfe9bd431d6a657d7b49dab0783e21e5d1508d9e4c3b5dc3bd4b7563947b0febec74c1e858c58e5e3d34050e44ea0137ead9cce094f34c65cbc88782813d3dfd17b8a9d59db83baa5cabc760d89f24ed66a11fe654b3592088a5561ca4ae34258aa1255f9df74750efe845a31d6d50f1b11be4bca7b68cde6d51445fb175e4ab624229247432235fc6bdff53e475fb76f7d47d7e3b95cd63bccd2d7912f5a3a999b6a3fb0fe567331eca1f210eddc3b0ffbff6b8622c6e1e1a600d63895ffc80b950b4d6b88a264852a938ccc0037d3b782d19982c9dcf664741ff56a2119503d148ed09fdd6085196707ba3cc9c331d407c28736d9c33f2eb5e8657c0e37cefe0c81bfd065a6abd9b8b582b7e445ab353b4e1a5027a09c8fe902236e13055f5681bcc6dde214db2613b2604a91fe436be5956b3f4d7c130d68122c8c777bf29c302381b52bd193ad93de61dc05debb8ad44285e4a86a4b76934abf7a2ef2622b36b3ea6bd1a4a0c44e2f4e4ee090cb226732ade9fb4a51ffb3d29a9b2be7f75ee73cc2886212345afae6f3fae6c3810819545420c3622ea70621352fc13d994c58695a524fd3497fd8586144ea38d196693a55b59fdc08700d44422eea9edffc44181f54e0b14aa3318a3b782dd40a3ebb4b7e9b7fcde6982f8c95da773631e213a630b6aae00d49ebcd764d28389b315234022beb68fba58f25b3dca4856d7495c5d7145d6946445fa956454df0b5adb260482841d12329a519d90742753f2cc7f559df744a93dfc0d00f2eb51a652a61e20859329340e83ba2e2cdf4e1636c113f775a43e290ff5ba67019bbfa49370f590c846056404c0589a530180b4c2d6ee50d017aa9f0c16d71038fce0eecd70582dcb0993b174e259805e9c792211f0059e7e1443f974c3cd904ecf36038f0f4d90b033ca4ab376e57a36ab152bd05c37b7457377caae6015652c4fa32a5f27f4e9b65c1d951723bcf0b0f4936a3fcbebfb613d01f31e9aed136b971ae7032b6e115ed0e4fd06a5441d368936d467457b7794c608815e979f156eb99f9cd36584486b0a651f44f6dadd22fefeba5b41613d4824976a6e0ce56c71315f37acacc9efa571639fc9725eb1dc5dea52e8f103651dbcfd44b37e4e8b0fdc9eb08fd39bfaa33b4ad182e8e40b5d4995f655cf092d7aee039b0f8442df6287a0bb7fcbc0471bee053bf46358ff64a99b152a450979ac29a2813e195ce91ed93df20010dcdbeb5656385ca13abe4d32e0a8ab34e07ef535ac1148609b80c250e2c82ee5d633b9d8f55b6fb88798f31ee31d97acac4b916a923613f8109a1e4fff3834f29f68a34d4082cc5e239d2636aade8846d85bb82785c4aab5b212331c1e37921b6f3ccf80def17a1bd0f938c5141d6442540d4736055a742284d2d51921790a17c80cc25c2cf3b90ac7a4754e26a21a760ba96143cd6a7b13377b8711d3796d573a893a30353e84a68db6c90319664ffe273e3c4917e5112571a419442094051a3a5cdcec4c86c78ef88ae2b7cb412f3871b18b02bc65423db4ab486debb35a9ec0b8d9c27d31a45ed752480fe1a4cb52f5142b00b16e0d8747a2ac6083dbee8832e2ac6c8abc88797bd2bc524c30faaa0131042ae62189974dd1dc64da1e65608a311e11504d2314dfa99180ca05985eced15b5d0277e545a23251530ab9d67151f1825fafa4f4f8c5d36aeeed091695c38fb58e2342e2163a9cb636451de45b5a56b79940b571936d49228372f7630cf147a0b21ccc345dfa74571c85aaebfe7a514cd6b0810112745f7b442603418430c5ea371ac2848d36412eb01d8c09d32b0fb7e4912d5e34c9c098c625def17bdb6f740742c027dfc13db32623d75e40c8bd6ec00c832e04b5e4e631ba3fb5a544c12f548644e0e5213d79a9813ecc881d9db1a707546ab73f0953c3732ffdc53bd928b6c772e30f591bc60ecf76146dfa5461cc4d370e56943fd9d666368179098c0d19b6ed3126010d5ab439494608a721bb5e02312e07822382b502a30ff7791b07827d1e08d9daae66d556e37ff26c4f06f52a53b494db44691311c890f8876d3298aee8c354a2f8b7314202b8fb95167de470af3e2f06b9222429583edc6161835d153430e8ae53a908942e45270f57b6d826e2eb690e19218f0e1c1196f23028dd90ad2c9131d243cbf3713c726025ccb08d4d74b4d45416b62b0520e6a70e80650613fdcc893e1c6fde0bb9cb15d7801626ef4a67dc18d4c418caea8c19a6a3aa838fda2e68733981d46428e025c0a71bbf1a2132e67db5bc0bb039b1683897b719502203d5dd9e662b049f43ddf44fc0dd5a089b27fb30cc2dcf4fd69cebd4293c6e25145f8059cb917e637c9c6cc31a447b4f18ad1a81bea013dcc004b05455d02f8f054299901d3ac837cb6228a5a994e117f6b9c3d0729ce19929681335f0cd1948ceca1f620400dd16a08003b53cd7e7f80e86686e512f26bb17a5f0f7c7bb50adc9f626e34f3d652d1c2b18151e3a884e4f3170941e903d1d26e2f4007017a36d6e37c9a3b492fce852ecf5a4075e7163b3acde68d22ef8601502cb8361f6cae9c1d69e804b169f667cb3189401471f545d19bfe3e56651e613129f8a87229bed66b0dbc3905f0feb0ccefa28672a96e923ce462465bbba07827c8b421bcb43afbb0745a082437d6fe7fca998682df28a8c54c557c16b0174d7048e745d3416cad7eebce7ba1ab82f0f39ef8c390ebb8a642fbc6bd4a579f8c63bbcd28b44e8294fccae4962f5f9a1a69b21fb8fbe0420ae0d154e9a8e8ca7f9d6ccae70d6c7b714243b87f2927ed394d9da057b8d8cc2383a3775cc2332780d68b5607dd721894ba3f81d4e58bbf028ac27db87b840e46945ea1f0ee511950f2488ae8e3cb72aa7b3b58fa5336990e263f392d787ae4e6d363cfd2b521854675a1e9a534a2e2bf0fb72718cf08dc12cfaf9bead041a813c91f3a2bb835f20730b4876d7dbac32666a0d4e5f676a448366c21d817045fe1612f98b1ed54ec0a0903f96ca021b2d81732ca08745738b561fed7df8f5f1a96c32cb4d3351265ec2a1a98997d019f45732f68b69d8b443294acbc3a2baca6443579633e112aedfd6d5dc7e140b6260755e8117117151674563c2d9fb9252362733f480f9c65373ec6fe0625fb66c902b08bc57e7b63d9939b9bf113ea13315f086b0fd6cdaaff343d6c89cc6311b07697ca8b44ce88396ea23d85fcfb4dacd1d09bd1cd37254babb4242ef549cfcc240535459418be8ac1c6fd5a0466f0e6b1e802728ff1cd5528b265a95e2af5e557bdf49ec667f0ff579ee51130440ff0e84a803ad96807d89f68d622198033a735b2e791d97b02f22ad8c188ed57e8ea0f2bf836da6a24031fb9ee091b4128896da216856caf433cfc566cbee142d240b66f7a6245983add7a89674a07af076ca590e30ab74e8197c782441cea10aee2072aaefcc14dde676cc9e801cc1e4a6f6fe207fe35d56328b262f55192d6ac1675155fb1b408818b0556f291972ba2e3910d3015ad6caa24046bb393aa1dcb927696e3ff4adce2c6043c0af447fbe2faea51afac4d99ebb8d993d41204fc39335993ed3028d3d52f61cf50df5a738ade3cd4aa66102da1d2506f9fcbb180a1aadb7fc28f17b44e8a9a328eee6ff1380c0b2904f58021f03a96c95b1446299475096197fbe8a4150a7d0be5a4e6dae05cfb94a956884121d019465cf453ab5cecfb2737f4d1db0c9b01a81c372b654d6e100cbb519d5f226183580b22e01dc46b1dbfae548a31106dcd20456d7649e8613e9e12631dc1308d80ea7c6ab25f6eebe68cecea75acdf9748cd8d48813f36286c2b87470dfd8d14406a78365344bd36038d84622f5556dce81b0072de533f266fdd9d9b8926489af77d747fc37821cc5d00af5ca4c48b1fc0b35229213d47853fe2a92d02f2707c7f3e843bfb5ec58e97c51de79d6e053cf4a382bd8817a2a4b87eed8a76136cc4d3de8796afe9a1e76ef028478302f178148dc0813fd6634bb4f5c92e23af4296f8407c6c8f312fc118cffc00b2f7b0b424e89e63cbc0dd3620f0541f7da33f1783983bf21567e72c78b7f476ceb2945d5b21c0967c0958daf43d20a3204a387c05cfb47208405e8381580f36987e3d246faa440f2a8bbc78236f3a780c5d7a06bc2f39d4f4c971bfb8f8b27a2a5fe355096919432e5d89ac6e6c4d5e8fc1555de6fc686bdc4c933c0d648df8f55738a7e2f8ed2183c5d1b9edb6ec88c88c81edbe09ef278743dca51a692b2aa837f3ae569e99e02cf978647eb81ff7da650cb3c7fd11ee92c0c82257d5a4655d1288879bc956bb651967915819d02020005f9d3590950a79ed98be4da237ba6ed138b93da34260a176ee1a31e51f647298d4a88d1d72aa627684ccb886a4b0c42365abc312efdbe458f733b26cfecb740400e4e7cb6a82481d10c5fce277d8adff929a98c226cd59fcdc88d4e136311145e9ac1312ab5285755cad7f680688e91741b3461053866c9f50f00f737d56b51d340cfb4d867df52998c38800643b1f50a4aca8fae529f013b386a473a0f2f81039dfae882748a2ec45ab7aee8f4ee4d73fad3774b101d456aeb884d7143a16a0c013539158803a555271223f2ccde601cd03456f4dd140788e2abe2ec3a657514242d77a2fee0c2f2d2cdc8dd11b0ee914ef4769162976b3f76821a15d414b4a775dbd9b811104b48914bad1c341c16c7dba3dc4be2f62d5eddb061b659072461caa9a728ae4d91f42622bc4eacdd7d5499cb27762e60255fe87ffaec4cbdecc37319d90144b7cfde762ad32ece850269e033a45e89bf19b69f99ca48d7e3bb15eeb1ec837d756e3cd41c0a3cbe74e4a0d31fe4f743fd5e486df7aef235908090c8c4ae714ff6c94917488847f8d0210e5e38a7d450e7ea8809ef9cae070f5e5556ba69020272a8529389cb860f3ca0fcdf8a34bcd74a3db2335f516d784f659ee9e667de9fbbdf0fe605ab84237e4479641d40b4588bfa16c29dd63634e719b565b9177d3245d7d4f833a7dcaa08896b233e36684cb0b6416ac563f8f3ef2c4f2880d00899010b6194564a8ebb048448b4918323af59f493d2bfdfd6ae6a9c224af62686b32ac9106576f8597505332742d2c888563d4118269d470a0e95b70dedce13087760371214122ed4874ea7f7fecaf7f38d33eb079c842115ebd629b3506e50f23f4ff0c486cb0a58e113abb7529623781a12dbcb5239fa609dd10b395908e7da4d2b731701d305d0dcde71d96f0cf141196934ec26c2bc3eb32daa0a48f3e7445616cf1ec3d422724771667f7ab7d8b4bba339de988035707980ec6f939912456a81ed2e9431f407a63375e7ef4399f84ab9f0a0b03c5c5d4a591a0699cf2ba9dcc626f0fd5d2893f09bbcfa2f7267867851ea782915bd749271983756979d2c8aa6c6452aed878c235da1e254a2b0a5c527b77d2fd75ab31979ec4b14b26b9772d6842e0b978259169f4621ef682d48716aaf1774dab1e3e49f32bff712903f2f935b5167891e899e91b8088b80d2715d74158a89dfba3c284c1519af0d7a07327c23d4d2c8354ba687514026b12e117cada04bdf87275f18f8993825ff5c468c6837b976ff272f88849c0c54732eea20408391fdb59554971ce8f13a4cc5073993179ac1ea010a63b25321f19cc0b51ccc5ff8206820847fae72086b361771d9374775a4da5f9b7a5c25dd67ffa7a4417a74a7d012463f1350df4fe0e406433c2139aff49d466d615d76c4ba568d66f30484f8179efb424d1582bcade2d7dc6ed8f349eda469e655a5b533e9f2fbba83598bbdf1c2852c7bf2e19fccd49a3f8d3091b874c517fcb2d458f9452eaed83aec98247339dc396de7f45a27a0f6a83942b735bc580799bed7b3f9a50387bb10bd3c31112d6807258f04f01c9305229d77725b3fde8f2873834b76d215cf891381ad7b747c062d6bf929bb58911c144059189aa97408fb4529026d6ad6da5d6f5e2fb90f2124d5e2fb58e2c9598c3e38dd266f0e9145103ef0934d45bc9127a0cc8f6d73a8985822cc0523d1a030b4b20d8862d3d8db30464c9c86fd505c4cf5eca5088d7fdbe37be14c7fc676bc314652b7b8bf35d8526fa53bae2e774858d63e3239261f3d11a12813e0f4f76b4041f2b5f74b5a9f52bbfc68b2e29d27661f3a477afd1cde70a59a2b7bea78e6218840cf3770d0411943d4ce5549526e17640b2943f0d577b6f65db4bf79ff196a51e542f4a365038da87cf4f940d2456d44a68b4dec3630e2029a8bdb42bd0b2d622e1cd3d80876db4a32a4b2829834e2fb84a500d7bceb42ad22398ef7418a9ff0ecb8d2758506767aec19051f9aaba3ea6ca325451911daa651d891247396f65b2dd09324db56e946a5de05ea3164b23f489b2ae434c7c9db3cc8943b103e4ae07ad743a1a7d85b524b797de9199985c9a31c2085165113a0d716fbdac77967489eaadc90650fe677156c454e8fe00884ca834f600b2c5024001c8c332d34dae4ba4d58609f0fe28660934f5b7bca08d95826cee127a82a7f1dddf9c4571cd6694aaedf37422b18d8450159d15cfe318ea9b0d9b566337d035f888e9bc19d5fc07790a6315a2a81f78121c965adcb20bb520416f75de8e755b96f9a9f17a32d161eaebe2f09e823cfb0738d0b4bea628ae7c88d3533802ca5882a7c074901d8ad644ed97a1310cff13d3750dae16ed668be9b9a64f4b57d2759eeb67ae4297019ba60131ea7bdf1277549456967c284db48a207a216d062bbe13c8361c72fda45a8e58994a686667fca836921a00b94af7852874c4e984d55e45b39a0332fcb5063c9d4e80996828089939caafb31603b9ad4a50071e942b548faac94343facd3a21cd5c908100f42b4e7badddaa0aa66898b7be5e3aba18b700a9e410e47127d5ec52bb6ca51bf61cc1298ef1207d27905e19fb64d26714c0131942f473292851da0668837db27e36e40b45f3fb6f323f2c5dac0228cfff281eb4e34d8e01cc0ef752673a09cc9d8b3973b97bd3f389b9571beda3d8390871c2b6e1d5cb4fb3486b8c195ad71956a7728e5f429282a9a158904a44f6556e1cbddd8a79ac7ae762e21462ea36c8d61f0616aa22e81a9a8b583745a15b4721e35bf351346e0e4ea365855601949369e06a0d19f1ff179136951f94658216a9d552372533fc0cf37b59d5928bff47d2c0caf0df79af88593157a6c4a55f018a1f4a4291e4eb4f7e7678938c690aaf7c51f5197ef900b4385dadd61a6bd8a38806d7ecc7689ec95d5a2dcf5dc082430970e4c337fb483eeaaee312283bbae14bc72dd0c1308a5b0a04ba72792957d419b812245518917d460a094ab1df47736ce95ac0fd67cfb8402d3a425d9a48609cc0590d2fe16848ed58b6e790c11a65ba87470dd51528fe05f49e8bf57b5290403241bae9de5f858acf34beaeb9f0becd40dbc409d57745c396efef84125096ce03ffb3e5b0eb34057ce518cfee1fe880c4bd9a9aadd5fd751fe04c7a65dbe872ea7ece7ec4aae5cedefb411d7ac02a345dbd5e5c66cf843e934866093752a566aff77f2dc1b545ec52906f09c4d412a1d60d5679439003901027d10f3e2e4e8b2fec89f546bd87aef4ef020d1a9af3948491b759c134df6e1d9005c0585d2073c792dc0d59e63c01710a6e9ec2833f254f2de9340b77efbb6dde2b24d202a338efb2b9c22bc4a132658bab7200fa8ea3589406198111eecf5f71b065019a822849448acd36a50654f66516379fe57b2703710d457783dc5ab5bed7a28b16044f87742127adfe3a9756902aa220dcb6cad5055799610f0e7ae63ec33efa0db58e3c9009bf2379513873001dd0de5f9f8f1941afa61afa2cd68772982ad064cd29c5a5c8a830a843895948e88ffd6e955c28a78de617fc62324a133aa693302ec26c52d5034445e50342c7e272392f0314fb356f2dd22ef6bdcfb58ddf149aba914835a77d3fcdda40dbe8391ac7f5e6437329b321deef1ebe82f0e14be89b91ab21badb8d6cb1f3fd412dee7105870da0640f54cdc9879b8107f235fa82be02a4d4787ea26dee698c7d889ee6c7ca270c4632d05660c1165618aeb43ca30fdde0c57d14ec71e76c8bed7fb29056bedd1b0f162abe1a8b83637c9fdae2cf8c098ae7ed04b1eb994933c8cd659de7d29d6146fe115a97fa9c9325cac99f3e67554ddea8a0b78989cbcf3f07d17728a7ec17d0e3a892968821cefc063ed156553f724254e12e209b3a6c55a3109d39d01f65b6da2a5a0b18a70ad991c62a7ffb8e8aa42ff1f437d12fd200603728de5db8aa16ba8b1508b255378d87c48f1ee477af8cbe73532728f240d77e6803433e9fdd750f06c7e2743f8f0a7e7bce2b1c86da9905f3ffcdad12dd06663de34e0efbdd2f72e47e7f740b18748a436ae7f96d71df14a16a6ca46fb9d66dbdc00228f04b1130edbed747e9b9c8e18b2e1960d5ca6d9d23c94c7f534cfadb2cd54a869a35f44ce680d72ec922924a4460aa85ebf8d8e3910ec3ebe61ab2d37928e5780dbe1c30df0cdc1acc026640c46c92a58500946656f7cba382d1dba229ef35f0b26fdf93e46559f221b38ab0b3f2b5699b85b0714e459c58b3526114eef96023e7115564d3b44124c2a03df61604decb386be646ced3cd1d6d39795295e35e6c1c581d002c6c972f8f3b7ff76553dddc1134f6eef72381014c029b94e927d6649b5c003a7efb6e451362e960aca39504848f6a01338a3d928a3f1aa892b574d635f9c59b2d5e7382492db0c24566e3165b1b7fa9186acc6faaf6f7c9e6205c64f02f891ff6638f2ca899a549aa87959f295bead540a436e3145c7cae05dfe6fe09a417481275d211ba4108b95ea30869c14a436f4499048596dcd17aa5b9dd1bc4c5811bdcf3828e88cbe43a525303dc60f7d36f093d93e089311ff16db3b432f38c41820e95371d1f320207913222a0961d10e73a2dc19c342ee6a7e614cc8d8cd847cda75d6f2d76b925d7dd7706e7b812b78672cc4ceb442a92789a061459236a4326a758cee4230407af3501ed3a842c3426998d48270bbc17eda3b6cad89893c3c11031d548663baf913d8ebbd4d5439339fefd03264d2bd60aa5ff233633dd2f03c0c250bbb1f0ed9bc519c72d749ed5f6fe289211440cb605223a9771080411454da04b4009924d56f1ef5fe0d599f6a9a5de90e8e8aa22b059114f75b5146ffd2af4b86bd7b3da12dd441911ba350824682681a12804e9119621079d6244e76c5b645b72c24a4541f66c48c364ec908f71b7897a16ec86c66b6dc072546d8be5dd2be96bdbaf4485194d200b42084cc6c230063b8603d8d3be18bedf749244603d18a26e7fe1f4537d95cdefab70d5a6df0a59c039df95e4eb9c8b20de725c3bf84672446dbb448e0264e8e7819e6f859979c38b7c28adcfbf6f015b0d8a7d176ddeed3a24c4d6c7aa59be24323317f5c25a16aa64e1d16b8d0ca0b085aaaa71d52240de3205c074fabb850256573e21a605f316879c9f8f9797a0410d1d462a6b476afdd2a9e54d957e00ad738f34413a5218d96973fdd4f5e30e3e3bcdcf5440522cd303418d1f4466191e0c8189cb7b4beafff84960253d5112a288f421ca9bf978996ff1973a828ffdf34bc367f97c87dcb08c4439cb2ecab9394844d67c5505309d3ddc1143d37004369b96f5ccaa950ec36baedfac418ee349dfb91153b97410f58de725b3d920ef0c425821a85bca00265ff324d1f8ebf2fe9d05f27dc3ce1a882a95b69b766dcc684dd69119814def40a3f7d438c200308cd6e4f82175a8a1146627ccf9f3d979135dccb0e7c3e9d03b920f0af93dbb12565136b576eec69d0d1a28c628aeba4a4282d4edb7b82e4f3f309ea6f4bceec0785dc9f2351e00eb6614e4a755ec14eb6bbcaf2c1ce4ab834c28aa6a711a377a209db94472fc05fb860b839dc218e05bfe07fd0b94e63ab42e12013487a6fc27b5d62b5b4f0730163660e99246f405621a4389946737e74620b10b508c8aa77f8c3acebb1d12131e0960cdceb340c4353c2a9b54ab483da33124f6addf299ae16faa3c3ac43b823b620c859e7131946489d83729000016d4ccfdd88880e46a37d15f21bc18ec120db83d5551a5fe89ed15be0753faf4c0278a641902d935976b472fb7ea5efcb6e983829610e7817288302c59bc3b2963b9b1653158913565451f8fe0d528250337e8dd1ba5adbdecbc2deeec7f05943ecd5b72c8de9ec097e9a2cdb36414be95db4ae731fc8b94d314519b0ae59e8625566460f0a83508cf1ea168f3fff952c191c6be0fcbb044e88e8b6ce09adbbe8933b2ae52e4ee5a8569e2f9d9c4c26d192ebd920a3d4aba4a4745d67cd6b978cb1300b071c30f66f15e1e17894f517d0d7f284d2bf3969c68f792bd80298444273ad3cd008253faf8e38a2654a4873ad263e327b1407d88d0b7f173315fb24d3f32192245985e4be59c541d4ed340d4dd149408d4365cace833dbe25f2c2efd58b440c6c622e750b118e366bef460d777fa0261418b1490700f99c84b2cb49ab41957ddd60662db35440d528d69b03307c65aa5dac8c05862a24c6a513c22860e960c3517665ce5bf94bcf8b7611a8ad5c5dc004b95e6627c2d5ef8b28944c846ac45455136b6a0a68dd8771f973122194f74db4306e847f284dc82b41c2f93cda35af14cd5bba7265d98c87effc24604cde805aae9504940bcb242284912f913e5b0582c87eddc62fe7a36fa3acb561e97fc2bc5a9a8a72be04803f5dfaff9af6c33d4146c1338426ecafedde552604b6960531f30547f4585c6a72a1b88502e9169224f95e6ec7771a8ad2b26616a1b6255cbb7cebd00b17007dc57f268480b98c79c8955c56d683566845f3c380b3b4791ed553bca66c516b30edeb06e0923fbfe80bcb2beb6cb5de2aa869b57738336fbb327839d711419bf6337ddead567a58d71565b635e186a5f41257aa7be9cfa02c900a3b68e38829a2df1435fbed18c38b2017d54445aba9cd51c71e57abe4e60d673a2ddb1a5ab2db83449643d5b3c0cb2c1c67a21cf4d36f975bfc7d77e74e9f0e3b8487816526199c349b598534c10609c6a75019f1499f7f7669ce8a0a14d07935e8a6c0e13a765ce5cdd047beccf345443283ea2b08ed03fba32441dba12dea892001e38236b6e83e2af612412e3ac7684a7c79612cce6bc965f5000789f39a701d293b5ba8363f27a3697cc2dab0e115702643a5ff34862d87e13900e9d27604847618c5524d3c86988a28f273366c383bd01b684edacdd1f5589106c372ed4927840f447aaaa3999977e8fdb0558c55f08dd66f482e6b255e922453c89581066b4942708592ed932bb8fe87a606f900cdb5b02307c425eada0e5d9943d5228d3350556f0a7a26135139644c6f333d30abcc00995965858f7c08109b9b5882306bf0f03be4c7471703d4656ac82acec3f4e5ef40b31a27edf335d735349a1ceebc6ca84e39ade0fe19e19fc0c6d2e027b2266288275fe5048f2b713530454fd46575a4099ead4a954e8dd7599f0f624f64984e6cf3cd06654e0b07a0b95fc548946d88055f450c8d7b30cfc736ebb1aface6be1e579f972776109dafea13b5c20bc3df0d3aeb58130b52122df2b4fe108de64e58233008648ab5c797fc12f6670200e5f1f033bf101d323261f3b391d18305580a2ed27ca03b789041035cfdf80dd699b5467da9d9130182e5df1310d069028045b499fce49913581aac06c24b1d2a7eed1828047685209fbe0ec412d08bcb64c1a011c25af0638eb3c1127eb009fd7daae4f8baf65270a241619e72f500d8812952ffb520cf77853851d1702c7cb61588a65f36cff8af926b6a085a756208944708009ba55499fc72bbd67df66890a408decb58a7e81c4327821d143b7c766f42ce9988c24c886fe11ffcd997ae9d7044d546167f68145bb64e5f3d0cfe22dfad3525b3f1d2d6b2524831969efff00b679b3e71c300d8f9b1a5bfbc69f5f8c8aaf46499c2960cb3d801c43597add6c885912c34b112ca930ce08205552b44b8d780e2b5292114b124d25dee01feb9dba4ff334255d00ae11e989804a480ea2fdaf50393424f09a2632658314d04bea69e74bff1849a2f36118029486157ba009c47fd1003be630b4d862e8148e584a3356805fea34179ad2904d993c1fd51c4c5affd5a7c0950e0e234ab5db7dab5bf0d5109cac9268681d685faa8743cb56d5d19f02b8092ac6e0ad675c876d009b922896d92f11e2079da567788888bb4878b04c904cd5a088e7766c8b5e80115c0f12d00e8ec20c6e3f074af05340bf50903583dc5dcdf79ced091650de89d72d83039d9301abfdea96bcde175d4d4a5e826730e494268a1caa0353c828be3542d300f359674838b5fde5614680717292fdf1f8b8767d21f06722823a36dd91c25656637e745090cd5ffeff04fbd515fdc6053ed10d02cd9ed9bb91cc036ffc621d567279d207fbaf35cb4dc94fec6c1e62c726ce06570d5f5bbe602b8d06c6bf95c1a303aef1e6aace8ef6f58866c72e284a16d7e97fc184b23dce854bb7f82003b6a929f29a3803f90b7d7fcf8aeae76a90276b97ab3c35980417fb543bc1b23475ccdfd484e86153a9abdc55915b68f0e3e62e4500aa4d60a65144d0c7c55c50122685961f6105e1935bc081c0508a5c00bcdd95b4a88e57b152cb9f59695786c5630e06e88fb5da857585c6d9ef5d550c2cf8e9e4c2184e23bcaa83942bcc6b23dbfcbb7db685f3f4446492011ec77d09c977f217d897b4935cf9efc6d79a48bdf286100bca1b843c842a711da4b47a78c14f6d1748036202c5be9e22eff88100ecf5a048e3617510ce4978f5ca13467195b27d516cc20b5513a995c9c2d10dc87f4e1913938a655425fd8c92594671c3ebc10b8d82fe986d30e495a39d303ab7576b32d8cf56c1475387c72e7e31248f109eaba48d6487bee4d48a20f19782ac83849756cf653631729ac394e1540b95df70cbc32907707890259c0c76e024920546d37d91fb090d5749681582bdb3a23ea2787a468231831fd08d4c5636bfd18f75016faa04406d69daa9eb970f8859c36d878641117c3a1fa9694c9b6aef5ff619f82a43230444e468f0a0343d6c4a9300263f37affc8774bd3bdd0a8c6b915533711e3aaa616cb683b6a4cf4ea38e934be92f4c0dc99c0640f6d8a26df4e01b884ab9321a86121127c784c887d13ee8d2740ee831becf13d991ac08f26f10d693c5fe93ee999239e19b3a3f1c36d7f20f341e383d4515f96d1ad44639771284ab620586be60d23f81f13c3f21217c14e883a09f3ddd8baa8c0cc8f5aba972bb17b621c271801bec0be4c700e4a9b1ec4da7fd9da1658c53171d7e31281ac7bfb481b6d6b3a02f1cd797e9e6a2f631869047a92dcb9e12d299a078d441ad85c968f291f10b638a967a5f5a727814ceff2d1dd90fa46ab2190ab9fa551a78c610dcfe201527b444f6196561daf8d54649224fbb59e918d1a88d5ff89f753fb6d3ef6b29378fcca46feddf5b192c3a06a07e455de7f01b5263457ecfcbac1fe1230fe52cb84bca3d51c94b5bc325e18f5e64f5f2f295f5a9ad5f9d9a32e26a7bc5d87682988c566f5596b10bff5779c17f3434a23810fb960364c2395a456901db33096947996aee72aa0d2a710b4aa4ce6926226b27af289ef077c36732ab0a586525bc7a798bf83a6ebd1d9b081d1eb33abeea22a5c1c31f122dc2ec5d6a6ce781d23e1e66f59933dbbb218661bd4050fc6085cb0b0594254881e297673a259ffa87f21bd1474dcb01ab335d62460836bb88f80b1c31d12e68d8e37c1e3deedc1bbb2c420e1b77c56975001d0e4f36db620bfc7584e4f744d144ce41511ab3293572b74c59142435b58003e9a8a21776df4424ac1008ca2112c20fe0355d2bcd2ad470ab2cbe0a52856571ccb3639cd74567b6f54240e27ec28f140581aa29acb99635ef478467e41bd794857c35e331815632517c569f6ebd17f75805f5b7a3121246c7ab9584f463fb3e144a017b7d9a8d646efd1ea9a1c142cf4de92fd4251f9bc0c9e7adccbd0e432b81c8319057b2c8f657b5de822e2628c078ed82d533bef7da0c92af55c50f238ccb71837f4086c6eeb7326152fe323271124807e2c95960775325a6d90613a5adf633cdf6e5a04fcfcdf311e32fb73af41cd002f5a8c8c10a4196894a868f68aa7327ca6d94c8082421b0a0e1760a30278e3076e6d032e194527434679b932e33ae007ec438650540d6de4aa62df67439d1a05a19f7cc47b90f045b5c2c72931140fcdca40a85c3c3f2a0cb3902f4d0901138c8238495d89b2ac08bdfb9e00a022cd45cd70e47ae0b481fc35e4e34ac9067e2b69e7324759d9086cfdae5d81f0edca1bda8a513d757d112bcac17ddebec6056a8d1a80562db6105ded4ef1c759c929484e8bc1bf3e692202af9614e3eb9c2d83bf69ead04f798b042f23a2cb93addf420442d6f51e383ee29aed35c2077b24d078e6f2d2c4037cd4145e965e5f62626387068540e158296029ee657b1e3a41a58f4c40b35b5d1a6448c1ff60e5221e40487cec261bb3849baf62285d41da50293e07bf6b9ae0a4d80a050d9eaf491ca1e62c47d4a654d60509bfc09490e32623db55147cbaa1d5e67ecd61ed697ffbe63a892f36955ef1c345383216654a637427882785bd9013e2df670ea789525790e6170402cc777d370d6b708c2c01d90e9224104dca84b0a1f86b587c873e678e08f7a6054bfb9908357b56267dc4cc3657890bd393a38be54dce3c29ea4ec54c64b9a13581b3ff47a143c8a692ee3733e481191d42e938515a5825f79e39d6e3dcafe54eafa51b593e7f2484dbadd24bd0acc3bacc265ca905ab39f0b3311c9595c85c6fb2ec0e7464e2b62b04d78a5fb79cf698a5777bbdd45322ae71bbd299871b38c561540726f0cf5ec808bca0015c8dc33faa0454b6633b89547418fa04876412ffd2429d590d059c4c86183dc578886f63d7fe3456f2b6aa24e2e6f2d985e5e3867c75e7e70cf5e6dc6d6648fdc3f279a049e35eaff18a5f4feefb4db115ec65d2d9996140976404f2dc5d6f93378eddd3a23c2717a06777d6d3a96fdf9dcee400ab5918598dd19bb2f571c42c5b2cfeb2a5071181029991a18ae00ed899a43473d2ab8e10dbdbd2db83dadb4d0d9d8426849921c8d311022993a42c2c774c5be2524dce282fa5f72d0ffcabdcddeda4b029245babb9fcdd6009a769ba826705ab86262bb30c15ef3c7e98e2e026d00b8f4e51a2632cc0c3fb8f85241d4a08ba41dbe7b89f4e30335116b8fb7adb32cf9b4553ecb05068df3e9d51f17a67f036d1fa6ca0d4d72ae1e43e5c697600f87fed491a0264fa92ae1da382c7311a05a76d90116f7b83ae60561bfa35372da26970d25a56a35b652a315c994c50ba20ec46c919fd83272067c8e1999570168f8d648a4072748804c8f736107489b587b3ebdd90f281cd46e5384b86c4d7d2a73cb6e4b01a9e142fbfef657308f25e78d54bef68ff9c1cd6c12c5306eb114491d1e16d19f0c1f0ad78ace56fc470e319ed7efc53e748b2a56bd470cc92795a0fbd8999dd73d852a6c3104dac0b91503c60932f54ae0e160381199e298ca1a2cbda1e4cc0bd64e4b563cc698751d639cd978741e185bedc3f787ba2de856671c1dc063f3bf548edaf7ece3ddac75a8c1d707616774a2469ca5f824f514ac80b781db2b46896052bc29869a59417ea54de1f4e5814a5de20808688f009aff38e3ad04c58a08805727ec28323418907c3ecdcc98a93562446bc06eae3cb56946c9fae067d9d826301cc08189183bf6c53cd6a03838cce56707fda697d83358a09b96121328064db2c102b4b462971a2f69a4dc1e2d2da10691d02b9ceb1a57a0551734d0a05d400b25b6f59c59850ea165d61bf6c3a80da50c66914cd8680cdf9608af223b0d7b8a43a2a3e7a5ffe9b8476215fcbe03bce2593333bca7d9e724b6b7dd163873aeb0375519ad4bdec44499193613ae53da20dadc8c7bb889acce1f4b4057c836d2ae1097d099bbaffadf3c4d84afe33036e62e300bc1c73b889e7fb5de0939ca901a06b974b885026891843687addf08d8c9b877d56eb7dbc8298f921aea796f6c36ace092e9eabf9d18629bdef7879303250f567a73b88212812546f95957cf701461d3d171864a2b3a8cbc45196caa787b508053aa6d13e4e92f9574a47acb1f71bf32ce9b4b87597b8b36ba30942538c73d2a0d2ad6507561613631cc0fadad0f9d29d5eafeefed2911b635190cdbda81cd7f9c8610f5c84bde570707bf8b0f62a788d2c3547a0b035f8df86069d8c642161b239e486344afcf4ca43fb0de3c9837bc3797beb5e76fac24fabf6721c4018afe6dba686bb10a7032ea13717fd9f29dfd1382e78e24e01ef266de04690b2aada619f72b6c274a240250b768e7cada46ab2a7ca0178337fc161691a4c4bfe49f96ac3275b7b5835862eaaa52985d853b7778866fa107c524437fcab0b22691655ec5a4718b82900dd81e8507ef5d275675a89f581b3cc4d69d45f74bccc4f546bcc4251354ed310aa2bf4e6090b777f0e6be8485d2bb38aa5352006b58077110a83ad607efea4c455936d8f7a8e96a2e6008feb03cc8a3974b0b4fc82ab7215777511fd801518227713c42e934a7116ba4778ecdfbfb5126fec2105cc27ce921d095ba66c1ac23c388615a816b81a032a4563d7d6d01e9e6ebfc86016febe69b548bd05c8464d58846534adca824199381a145c4efe6bbcffb1312210c35d847d18f3ef67d624aacad1e8a2f0e31bed999f01b41244a6d8eb650b8041603527144ebe2ee9acf611a06102635ab161fc9a30905b954c71444536518cf4d1a70ac18b8add63ea85eb61ec5cb4b9d0cd93d5c7243a77762c01bdf30d5171446188978b58f8fa31e2535eb512d60e527cceae50e28edfe931ac89e8d3014135dbe00468934d64c5e92aa2abc7d81406edf1beb4c45b1264f977b15c1f61e0b83789be8c80eb5ccef66a7f003388049c1c5646c00df7687a17e89dae33d13f15b66f11c6185dcc1a15c948ba5b3c88ad8705d94275b73acbc1ab4861bdb7cfcb3494c2ddc70ddffc3af21f63727b32209a5555ca458a3f01b4387dec823fd9a4535457aeb09c876a491ef051f2b46e74b308abd2450a9ca5c25d75dd52cb5eddba28aa04c20a6f4a7e343dd385fac7ea13420d93c1a9e27d40d20c5293927bbee9445bac471adc9723c5d19bddb2846856a5f79c107cb7429ca8d9218b9db820f2bee3a93182068fbaa62f87107b8fa6325eb2a6d93a4bc814eb3347ce08a845ea03f5d480d03004d0efd9f79ffa6183c38ea18e00e4c38dc1d6bd609330ec4ff5ccd5a5d012d2a2b85a4aa94118d0a68615912e5221bb7047c649e095a08bc62925d018413519a0adea6cc6d34fc1828f90283b6aed6c668498900308ffbaa679996ac0b3b8948edeb0fbac401689f1af5ea7d3e7d0992a02d023c1cab88808f2e2515f89eb0457f974a843180a8ae65c8c762be2ee488af54575364f574494589fa63883f47a1fc6d09df3063cf714a8d7a04da4e9b616ba5835c8cdadcddc878f4828f47c90a88d678ae0a9c5425acdab5621b0eab67afd07262b1bdac77d67d4ccee04de6ef02004371aaf08b419b08070ee41bd57ea4d0861a00d57475032148d3c0b7c2c071bd486da085f519e9ee015648fc17eb943ab14e283da4ca3a18548bf4475f6fb0263751b94a0c842ca3c295c6b02dcaa6cdfffa300d8fd1162c03ad85b8be113fbc6125baa4d3eb2ddbe77939a9a66630c5eb14b961710aa88cf35fc7b1409b736c15b4c234c50ac869eec4d8ae2953a678c4387c506b9e5930bf444b9a8e52c7efb54aa168c17a132af9d7407afb9d461efc4edfc9c787de69b9bfe9a7e788a6e2256436f44b2c1b3ab789831b18ec70e8222c32eadc8b77009b5ae5facf09106c2d4900140668188171b493550f49ba33247bbca4ee6895bf4f03279c5c240377120aec4ead553b5fe4e3cefc29e658807827a2f288623e33ad4fc7605579d2d731471ed075173ffc58aae2b853da707eb078231bc25e379d6f49d927701170ef4a077cc6fdacdee645506361197a96863a3a0f7d962e995d20f85892a18ad7fe73fc75ff16ac521088af91394e72367a8154425bdf2462cbbffbc34d3483fc760a4ca19f661419ac4a87f211235f1017b4b131f0186e42cbd4536a945f59c53d1297d9e12ad48f242e51711b8fddb42d6834a1e66c133637b084343eb4abd396c299a6a32793cb8a3b658933a4bff63d2d34d70362c713bd5a865d35aa80274f384b0d82f9ef221f8e5c37370793b6b77a541e1880212a852a5769d3810650ea628e92d97ea1e40dcb0ac5ca0d2178950624acd9e4ab926429fceea88fa044853ad2de41c01fdd724ef61c2ecae1f9ab19294dcb7f01349e1fe72e72d95c8fbc4bb575d1a94f556ca3a6b3c513fc8e4095cda7e1ea1ea1f1432c7f893f5258813a0709e7e67270ab4266ff03dc47042afd5fc85620d4324a0789efd3a431a2757954728a03499e1e3fc642efab6316c0b5a16503d353ab86760d831e2f7c07d19940a0e854abf036e3d7181198fdc52b876f7cae8469fb5ed43602b768fb44922df7265bca2d654a5206a60cec0caa0b3700a3082c702065755f52185e24b1450936b090c38bd5853f58f080075fa444c1032ed4585d58e5b52218a187214431071274e86175e1d06bfee078198f328eaa2c19da33d871367a54569f98fcc96b2aeb14c71d5f20e10a2d444920b18a3378159a7071a405110f4138595d5388f9cb7954d6f5e3ed145ede3a4d6559fab2b8b101f3f1ca144d4861c5479fa9ace80838020fa270e2670d2348b18a3258e2832bae8863081f98b8c10fab2945eba7cb54d6f45365694626305ddee4b6b24c6730f96d73d4e69eda7ca77bd0e8e2372240dbf46debcd1f930dcc107e3b6d31d81c06b58aae36879b5f086573ba5919278618830e315ad840880d4a1a72086124e689113d58dd47d40224da806284509329466cfe5a550032e8181a43450e2e8ab07944e3081f63740ba6061fdd545911f597e63b4231c5b7db27be1d55c79fd1e4db37149d5e2b6b9ef0971966fce55a655d59656dd5df0f5fbd54595589cfb21d3e736f155d659e629ef79907619ee999ef748f51169f11c9dc08f3b467ce81fbc07c76f49927619e77c50c1d3e0bc39342e630083ac92094cfa0d0676b4801e28b511320ac24b1ca8ad8a10a248cc488e3c9eada56e6efbea4cfe0cf6763bce6917908f09a6395a571f1023bec38021a5060b0baf663d4e1a3d356edbcc14694317e86d0441aab68458b8f4e2b2bfa307a827551212fccc822888a24c811d44cbe8ba868b186134078414417ab1ec211befdaaac76e6c1319ac14f9795352b2b93f22c69f425aeadb4df9ec71c62d49bbbb2a3dde214ec7ae8c73e617a62cc9bda74f46c1a3299b4647f4dd7744d39fc35994ca66b027a365d93d0b309cc1a4bf8800a1c4340205a5d130798c42145174d7041460e56d724c426c498df5a68bb5b0b6d5b1a5a6c77bb1b0cfe6edbb6dd0de879bb9bd0f326458e61828e2bc258c28e1c5677c3e10e29463e0821a10742aceed63a3c21adc398af0d541ba83650ad6918d55b6f7dc1df5a6bbd15e8b9de2af45c136083222875cc400b399858dd7a44250b0f4e80aab0026575ab1b4f881b635e43d29034244d4b638efa57bbdad5e0cf5f4dd3b4ab013d6b57137ad6a43c14587c300611bac06288d5d5866400eae10b25a811a60c365657f3e209f162cc674559515694653fa7bfd9cd6e36e66f9665d9cd809eb39b093d674d8069f206d01365003559ddac075ad6a872c7162b598058dd4c8a27448a315f6a955aa556a9e4c3ccdfd22dddd212fe964aa5d22d013d976e49e8b9246549105a2f4842830837e8218cd52d0d1141e9210934b8424411ab5ba2825179e8171bf38c55c1902e96f4ecd8cfc52e76c75f0cc3b0287fb123ec6240cfd8c5849e3137c410a248a8080a0d7a585dec054073b461441a5f76a8b2ba98105488314f8528d2e597fe5c7aa998bf94526a74e4c45f7ab4fda56d3cfbbb14e8995e2af44c0120e68d295280310321a8b1ba74091473405102152f3022ca1bab4b598727847518f353cc33d064a0c94073629977dea984bf73ce7927d0f3bc53e8794ae980107ee042d403264460acee7cc1185ca020c30b211e68b0ba938c27848c317f195d4697d1755d579830979767bfa878fe7b31b95795eb5e42cf9794279e9027c63ccb21392487a494920b17298be85f79e59576fc9557023dcb2b859ea51402e0e048cc1a60cc012607ab2b9f1944a4f1050831c2606287d59590064f08a4c198e70883220c8a3028297e89609e3d7261158c72a397678f46f36fbcf1c624fc8d1c839efd7194d26a1224c411b2f081103fab1b879e851006890ca11c8ac4246175634b054f081d639ebb864eeadb49cfde69c4bfcde44995267af66e093c21773c2cc63c8b79183a07fae523a4d725e9d9990bab46c0ec4501635250c01302fdc23bc03cf3976767369eff3293677f979f30d1b3bfcb40cf1cf4504c0f84dcd339906f1df3ec37bc14ea13b28245cd744554e5f9c227a8bf90c9f3bb7214e6d96d7843de339780bda8709c0323b8a246936706830576cace3dade219e20d38a0180389e8072b3ea306627210b4430f5d96b0923f58b90309047b1be8a1471ed8cb480f5d2a612f173df47887bddc7ad842d0b1316cecd94242420ffd0949116a550f5d2cf490e765c1428497ae020b2ec46084556f75f497f69012ecfbdb430fe1f750b798e73d74622f034d11460a7ba111135c1e7aac622f1c7ae8910818e5a1c721ec7564ef4b7ae8f40a7be3532bec6c62af27ec8d7f39617fbebbfc3b61090e84472cf10684c3a868e7bd2e8fe8b57aa87554ca77588d6623ae01931e524aba71d1f164892740330c67140916a550a8bb737bee239a23887f64a538ac506b51a886407787736010be387c43a3655ab4c2b65f7254814e3c0a8128b51605e44f342d5a61af36ac962d4dfe3924920281e8b5d12fca533b3ded2c85792e3fe1272cc43cef4bddedc1babb43bb9bfad75dfba8efe8fd91abd4e890b5677d03cbce83858f5f479951f40d7bfdf57f349cea9e0efb331a23d341ee601b100516ad2d6ca037200a2c5aab048859a36503bb15e016ab48642ffd1858e807e8418bd6eabd6fd5e372144652e6c5eab98529f0e5c57a121865028f5bffb97caad76f48fb73092f29ab9431c60821841cfcd72af872a063f4f4ece399ffd5f7746ce038a81b9aa77a8f637ca7c7a9d8c98e2e69a79a1356d334a63cf39208198a8c3d83355dba74e9d2a58b0e20b7d67e77523958111effbc855a45bbbb0f8ec74a99a6b55077a7527f3b7b0ee294111d69b8888b8210e5cc7b439e16f065fefd80444f472e71cdd3a4da7b432ca498765df0a5e447177c6d21bef48ce1e397dd5df21096b49473281fcfa1501a657c29f3fbb4e85ee740a112148242d42f14a2cf414f273e2965da0f9fa7e384cd34edbd2ae8039e8a3a3fdf6915ed388873f81181a390a6d156517f4e395b8f9058b2ba69287bba98cf2bf25498b33f54abb08e833a87c178f6f75498dfa70546bbd7399683a0b0d02f073d7bb66d3576e50aed639ec83c5dfcb958864ad63cd807d9d495954518f97ab2b2b8bbc3cd2bc35e33064bdb5df2d7558bb06bddc4de20afd966d84d031cf3d7050f3bfa74de9c18562a65d9734d4bbd57ebb69928cb548bf0c74727a4954583317764e676c9d180c3d300df54ba7cb0dc5d2dd3aa6933c1b9dd25ff38acb2d8334c7bdc4d7d06e97697bc4d0bf4e1bf4b9662a9546a2e95a0d6cfdd5dc1c34ec2ac4bf618fe8b2619c42687688d3d889aac42b3b884587a6f6a1715946a510713d3e21316ba2c69b108fb62a6492a34ed8ac2c2f7b4ac84d16ad342fdfeeeee0a9e7a819f99a2b7bff6ebecdfaccd89f567bf3088fbfb8eda4bd0aa7f39ed56206d91f42f8c9f6fee8f7c9e4e91ff49052a9238f5e251e49ee9a402155d38f52e7feec3ceefe9c008f0cf5f1d0b8ff3e15438e2f98481f58e81f5e908ab4438c99052caee8e9d123623468e913dc69631cac8fdc5f9c7e6c36567eeeeeebe648cdc5b48e8524a292f29e3438f76403fa940c50fa7a05f1df7e3c528a38c57fb18639c1d0ae24b251de1b8d06f84d0a1dc42c580ddc52752c6286747298d31c618639452ea0005f0a966658c341fa530a768e26791217c719b81c6a7e3d5511b29b5d63eec0d818fe6a5538b04aad9f71ecf7ba96b32ad20ffea689e4efb0584857e69be51005fb334090082cb1168ceb0b333cdeffce9f7fa297f7a7c43aae6b7df10ebd3e11b62f2e9fc866c3ebb3b8356c74b5d967b4f47ba89db387e3ad22b179f8e748d939ec9975eba5e3a365f7a8c73c2092f201a7f2ca7fa6262a38389ff8c3e6a349a4673f985481ffd4dbff0081a7d84451f1d76691507754e7b7448d4aa92c7eece009352344bb0d1af66693815249d76d7f6ec2eea2f07f95f0efa26facb43dfce9502bd2a75d0a873daa914163a3cd22c8d84491f631016a08f3276362d9c0026c51f29626487ad65b334bbab5d979695aecb237fd5161ceb6e8c00f56bd278d9b4c057b702f9127edbc18f8af4c8c98e637f1f2333ec1ac22c93be27a75f1717c5d82965c93e06630bfe4a24903163c6c015c8c6e2d369737797dcd44befcc2fea5a6f95e697bfbeba3b43eaa7c7c96974d8e9efe548b79767dd4595ba6b9b3e7de8b186af2dd0bffcc84787904600c83fbf26f7fe9ab39b21e583cb117ef0d2e9bf9afad8edc49f2c72aa5bb35a8caf6adaa57949fd6a2faf54e734eaa5472d4c8cdefeba4bffc59bfaabb37758e834df8f791ed518a29e005e24e105eab582c034ed91692a6b06d85d7ee8d12577519a85ec11d2005f3e79654f9395c5dd5df2ec37379c8aacc2a1e4b2bb3492bba9c34a7fb508ea86534d45fac37c3ae5b0ce33a94de9efa9c7caa22d656d4176b1bbf1af8e5eb1d05b56167bd708d06ebf995d8cd7d5cda045e07991841749d8b4c02e9d5d7627d0e24b228de60a138f0806fdfcfcc8e011fd6b15c1c923baaeebba845cc158ac60d182850b162f3b889089288990a30b0fbc5441c51522266004618b2bdac8e20c1e4878015fde1b02c46f7f3bbf216f08d7b5f0c3a15f3f9cb98c83d0717e74f00c2328847dd6223f7cf38b837e0257bde3649ce34eee9c75205c8d038033b9091c0fbfe168dc869b71de38cd7ffc809d11eba3bbf451dda53de0f4fb8e30efe11d12ccd9213ca34ba519b4af5ecabcd4d50e09fbf49237e6dcc321d7a3b3a10eddf20cda4fbf33685ff2cdeffbf2b0db7cb8bc7a0d741ed069a0cf9c38cbf1d3816ee2e2d381dd7424a5ee4ef75649f6adbbefcb9f015398f00c28e4f2ea46aac333aacbcaaa108cc6c5f7cc1836002f655c62cc99c33086670c807b38b263541655deff5d59234c1b42bff3ef0a1b70fc1a60be00ffaea8118693d7fec6919ebef1e515f0efca1b51fe8a1b613ef5ef8a1b5c3e7b28b0efca1b4c3e7be8ada719f5f7d5b7ccb7cc4d8028547777ec5b7d217b586b911ac7de63fe983167d72a8b91b2cac2ba977135fe6864ac6719eca663dcb58f31f40c63cfa067181294e3f090bbfd98f788984f43cd39ebd1f9f0ab2f4c86372fcceef2a3fca6bbfc5aebd9511974c81089e62887ce4f75a98c5f6b258062c7ae126983f2cc2ff7711ffec36d9c1d66bcf96095f033e657e6359c568bdc3867f505eda1df64367efae1333e9c07df64d84d09eb2ec4ea0b1ae636dd8f0eeb7c741945d2e4a3ae475d1640d638967fb9cb96d88003c4ee0db7b9fcbd3ce390d4380ecfdcedef9f0e39ae8798e7f2ccdb0a4c017a765bcbdf8b86fe0b7d38d6172e7f8dc36efdc53ffd2ae1878e61ddedd65fae849f31c73acc2fd6126b89b5c4d86b1cf3dbb265cb166a9c31f6cb6b1c7217c6d73876585f90cf8eaa692b5008f4598bd4f8f48b63efc16dde9c090eb9cb7d7a72d56b6a7a885397778e741edda534dda5339d4c77ed9fba8bb2dd45994cb3949920847076484a8e392cb9690a98c2b5b16bce3fb6275689fcad4392f974cc2fcf1a4218819263268725377536d435f7e91ce92854ab30bf94f443bf4afa339f7ed9e8b56ef3c1fae59b9ba0a41ff3d95d36facb672d021d49d64dbfea0b5ae632f38b9299cc3087ddf5cd7ba8a78029bcdf5c56d6d6adcac592bc9e5e167cc9b3a8516aed7b987795c0ccb08c9bb7eb822f39c695bc6b0bf24b5d7cda42f6b3eb29a090cb08c53eb4501a398d53ccddd44fccaf7f7737c6795d61fae53e4db9e83393952525479fb9eee8ddddf4c2d26cf91b2d6b912561666666e692700a3ece6426a59492593e8f5dc726969d1ec12e69c854fcc040f8c24040a42c700c88861446b03254c0244b8bdcb0d388a1d09430e1806e5208f5084d3241b3d21a58153488195b17102c7419a33033b3e9020273941d329237b7c4d729cee97c78febba2860d9a21133caf88420f9120800142987f150d9b6941823d690183348ce01bb6febb92c61c3bb0a79f2a76c607216ba314613727826cc4626469113761e75fcb4358ec2f0a7009f6fa77458d2641523cfe5d510388cffe5d51030a34b88b1b0151a744451fe98e11aa07854464f4f4a943a3a7a88f1ea53c7da38ff0e1ff3cfdeef2108a877e07f0cf03f052e8a12704fa4b42073fe0524515568258a55ac5015911c58e275fa091c573178af8dbf3cfe9f3f71ce70d897f829e477243fdc980fdfdfcfcfc04adc888994bd7aa11825ee2bc4c05798942cf4bf759e2a52699c8e7a5e8a51fa962cb4b30347edf511b2f5f18e9f06789972709819af4f00b9d3c79e95028cb0f2f6114945f28658a0fbfd009e9b06545cb4ba8e587f47721979736b248cccb928447b08d1bbf30cc85492f9d9978e9ccc404bf0c24fdb2137ef2d259c889977f39ca4b9632e5a5132fadbc746e9511c74b2d2f9988b9b017fef2d2d9a8072f69241f5d6e83c3b0989f31c6a0f9db4c809a3881f2d25be80b115e7694972d658a13565e7ab7c438e2656b21bacde5657b79f9e5a55fd7e57e5dd7755d17e7d7755dd775757e5dd7755dd7097e5dd7755d178e5fd7755dd705c4afebbaaeebbaaeebbaaeebba8000010204081020403c0495050407070707c741505938279c70c20927f891ca3aa1f30f70ee8152183bbc7b072acb395059d79c1388cf39e79cd37dce39e79c9ccf39e79cb3f339e79c739ee073ce39e7741e9a73ce39e79c73ce39afebbaaecb375059d709279c70c209ae81ca3aa1f30c706ec41d03a72f503c1097a1b2805ca0b2a6091f1d15dd7162a4f2f12646201e638c9e24c6e81e638c0ea5c418398f3146874631c6ce638cd1f94984c2436c25461a638c31c6188562e8baaeebdc0295d5711cc7715e81cae2dcdddd2950590e04081020401c86ca0272396dfde52f54d635812e5f7a407d0b7d777b1014c208fa36a1876837c281ee30a87cdf341027dd6158f15dd39088766f875cba1d1a95f10dc1c0a41fe6e1983471c2f3ae8461c73743f91ea29d8798a7f376b6c25abeb97c3b77110307dfece59b8fbe9d91384c8bf966c23de1a58e367af0220677e820690a2e90c8a883cb96d5236afdbca42c52d660b2450c38c4e86275e1909124aed8a20b369e801284d5855520123cfa4e8243088d2326b420ad2e4b198111ad2f61f08006105fac2e0b7d3b1b7dfbbb5cf4ed73674e77316ef0d3255059b3ebbaae73172aabe3388ee33c0295c5b9bbbb43a0b21c0810204080780b9505c4afebba2e67e172808442fc10c5208e3ba608010cd60e36d8a208c9cb0f3d587dc9e18b0c3ec631e0f8e8452a2b1aad5059dd404520e2655c4646c67182300f1097f19d2ec2182f53232323e31c9071191919191919194e464646464646a693919191919191919191298389efbc735665752a54d6c5c5f19c3fa0b238bad2792f8212effe95e531c618638c31ba4f65c516102c1e883ba0b28044286458f1ed0d604065c97c49e34fa720fe74729c20ac8aab93a3c03cef4f5fe2f8d3e9a48353f4d3e99d3c09d0693a817222a3883fcd9c1e97d3e527875c4ed0cb9fbe3c32dc288219605cb1a30b266280d4d822872580e88196d5c9c81a425f84200219626439792183377230c614742449f173f293bf1319415c4ea4b22e142503063f3d85ca9a31c618638c31fa90ca8addf37d53df2d832f60be9df61733be7d0195d53d9575ea79008891840780ab2a0b00269860820926986082b7f12658d9f9e83c9515e7b7d316e3896f574065350a9565c2cecdcdcddfe0fc8dbf1b64017f51f89b9b1bbf71fac5cfdfb8457d51e56fdcbfa0e36f3c05c6d0dff84e1193bff19e1bf72992e26ffc79d1fa1b4f4065dd5c21c5db99b740bcb5d6fa4edb2cc4bcd5b14fac750e5820eb05e9ed8dcd813db975e8c4b6adf2f3f3c383f5a289b79bb574651d76619ef7d65a2f608eea68e24b194cb0ac4ed0628c1651f880490d76b0bad648942d2d31434d88d1864552832040818413287ae0c20ee18a2384e1c51184bcf8b2ba2f89046090b420238b11b4c862fd59eb45939791710454960c75c08d160764795b1e1700fc7d5eb488028b167774b1f2eda82e777cbb6fd1faf614174cbe7d870ba26fefe92206dfeed3c515dfce82971a7c3b0c5eb8f8f6031469271cfcedf993db3f51d9128609a0273f441183d54dfdc95fab2e30a4c3186fd8a1031908b1ba3b7f3a398a791cf027f725fee4f4a405127ff29dca3a35c0017f69dec5f91804858f443e7a8c4478e22311e6f8c8a5898f5ce6f888c5d0472cecf89845141fe3f8e8422a2be27c9677537fedfc75fd70c5197f39bdfc5dd1e52f3740655dadabfd7cf7468be99428899f6e27511c564cf1f3ca1534f82ac6f8e93a3b412acbda5849e26dbc00956513c510ff83c7fff8f1c3717ef84effb09283ff31f3c3c90f37c23c97ff700efcf8f1e387ffa822882ce458e387cb11597e74c902073400a289283afc70d55f9a28a0104014461f9d009515511675398de2077f794e655d8ec514e4a110dba910dcf876bbc4b7a33cb503c51adf4290f2eda9caea9e19babc41469832d868e28378031f674c59820ec0203a6288d51442949f3e80ca9a2e80cafa6180f781258df7e101a82c1f281b3cca3d85f29d4661f9f2a80ce5030a858a2894d356b110c411b498c2851c5f7859a1564668b9e288c91537284205c0c8083c80a2041d70d8818282042272f872461943abfb88a43c0acb131f3de25456a49706e74a19df57a2f8f6132aab515d65a1907a488183efe15c65f5a87114f3a4f035353535be53e3b4c67e8dbf187c8d152f3e22e0a37b65c5eeee762095d500a8ac1a1e54c8f13cdc84cae24113f434eecc139dc671683c08f3b4d3f84ed3386dd54c1c45b0230836d0a0c10f2b9a96942d557491c3cfcfeadaa7f15784a7f130b6781707c8df20ef12f99cbf4f4b16257e6421fa76949622beddb5c8f1eda92ddfbed3b305cc37154f7cfb4d6535ffbb3d1fff3e2d1ffddd57f4b149143064f04085955618efa63e7af4d7a5061fdd77f0d17da27c0482071e8030424c1b4530586581f2d16d2a2bfea82c9a197f5dfc8cfba8ac9929b6bca6f5f09ae638419827bae63bad79cf1baf11a181e6465a45579a1fb19ae649ba78ed8569a25dae69274d73288579de959696d7fec229aff1f017b65e7348c43ced9a432e1af4f25ad16b505c291223096600e187556a063be04117252019a106ab6b5ff3a7f92352a34b9655d3aaa6d917a946f3747a88f8cc2527deb660c84e90549a2a4bd22aadc3e11cf60ce7e9e0bc21f5d9b53821359c37043a7b009e8a8697c3ef3ea48bf338cf58a4187afa0f8b952a9f4d0de70de152e9a20c747474745523768ccda6469108037a122e62a27ed85d78e5d9298528947b2af590762e03f9e52e47474744571df6750eec38a83210a7d8a30facd655e91cf60cd3302b2c3be4665e7bc86586b1b04b078938c5d4087b19e888819ad2a35651fade519237accd7be3914887b2c39cc0ce067b92140509499f618f21ddc458206ec21cd4aaccb9d4014dc738d40da71e1203bd1cf677ed43821d031969157d39ed45124208e175c9795df3ba188881d808e31e3940d8f5217c0f3e18db2f8d7e6d4c710e747ecca55fbffca228c6613bd8dbaad4cbd9b9f4ea6e8f95dd7dade6f8e315e32523dd41fdce98b981c02b56b45f20cfce72065aebe7e707cc0393dab1a99ba24ab0edaf16813e2ca5944f67ca0183111862e02f0fb60b00850385b0432db08bd41ac286a8564122c68129c02b4f087b6711c0b3c3ba427cc3dad03f92a45572d53f4097d0cfcf4f132b46c0b3c3c06ea4556fb5baa899066dc156c35696a1217c77ad9d56f1432d45eac30eb69e0e3b85c396fec29616e661b7d88f8dfc10c6a7937a5d60301243e8d9b3f7d8e98bda7bf2f4380529b51685724fa51e51e7203d246aa6d45af79d1d2d5b881e12b58a57706727956aa2479bb05712d68ae783e80df1f15e0e0ce3e110de1f1af15435700e3bf5818542516479f61ade102a16055004cf084ce13d21edec1b781f783cd1b91675337f5f517737e7e12beaeb72e75a84fff23b9dbf397e9467f041d9dbe53ecc4eddb0b042a156412bb678f65218cf4ea119cf8e113d1d2eb25e43aba0d0cb612f75420feb92fa5f962c5f3cedaee5930a5500bd2b4758bea7f9f5740fea5f2d427d2ec1e6c47c98ced4d309b2038920110bd5a4ece15cf8e249852d439c7acf856525ac3cfff5d7f2411015a1670ed2023f24e214ec8775d7dd7b3005ec9ff716244ed197c20d1402b9119400044f0823890000cf30702a06ce61093c21ec2f3c1df6cb1feb0af5bb73e1e9b0c7247b219367d7bc3a9767874c2090c61d812e53ad62779c11708a1dd622f5bb3bf274d831224bff3ea311304f771abdc362586718d675ed5e8a533b28f4c8930a540071ea7df4257c77d0093bf3af356589874154098a6293774ec7785d315e514629378f6e8aae9568f413bb65376ddc7b39915d72ef974b8ebb0ba15f1cf4575f883d8ccaba4b4bddb518124894780681f4ed99ff7b96216af357c37cb9a45b5cd330ff8accbd47b5aa7a4f2df3f75430744e3b7be5decb995877edc3eaec1bf73a87e10b3dd4f934aa731a86f6c6ba8b427d2761d9afa6b221c9cfcfcfcf6a075ff5143001d01b721d7771f8cb87a14c2252a4125f663c3b65942228264b252aa3c8212c9239295692254afdd592c4a89c52c6b883196511334a2977302516e9105362119398c4e28c524aafdda55a77b1d8d106f9e7adb2fe3cf532cf66f7620bcd5af2d9414ee341d726c222c95c56cf784a9ac92bac5ceb5fad5383a51981f859c3d9a1c0a9f68debe188a0d04324e327a7909cb7b57c1b612fbf28341116faa8c0820b30c460847936d7fec2bf3d0514d2dd25ef8a1b407f5fcbddf8f97615b8f4145008f457b5aead4021d2e50b72937e957475cbcdee668dd10ba72b6357080d71e18fb743a06efd0f0c629e2790c97cdab66ddb76f9c99ae005b76ddbb6a7e2c1a33bb2751705bb6bfdea1c68bb6bdbd4752af319eead1e75ac94b9ec6eea7dfac8bc2a20e3343ec3c195e41a2bb9ccebe0d5753325dd52293397334ec3c938fc6915fcf9f999f1194e668748a9bba86ffb282113d147bf20f398a6c7e946e019108c1178468460e01946b21337e3b5725789bbb8d89a4c8a611a89db806f041a75100ca766bdb553c25ffdeabaae0067fca27ef2bbf9f42b7575186672eb2d3df313e7975bee714ed665993f6ef3c1d23b83769f4f6ef3c16edd55d2d0e3d30e093fbf2fbafc385ba81d12e8f2a78cf28a9c7a5a36bdc4c5193e2ee186e6ef0c9306f8572635cc37ee081919cbc9f8c5d523acfbb0ac1538f98c7765d58e726acee93e9d5d9a89bbfc0899ca5d5ea2c0c9672c27e326ae7a56e2a6c37a75a78979c97d382b7197e64b3ec33dce2979e528a74a2ec3699c53725857c07c863bb90cc784cae63bf88a0af55769803ffd08eb93639db8c8391a8abb711cee08205fd9ce720e8460e0197149e5131459f4ac89b0482e97df3e4794f7f1efca1c409fb1066b0bf297d0286954f4e11d4eb50f6750bbb4e81042a8bd807a0f7b3e4a6474c84defa1b63245b762654d249f8e9525af2b0e4d7f317ab72a2bbe17b21e8a1cfc69d591ce694739aaa6bbf669ba9bfac9bdd5a5947ad698d31a1a09a76c9ae935d47d7afa112f699b5be74a1dc5c1558faea6bbfe94e692e92e7d8a4120aa75a5d9b9a350d7d61530bf9c05038a366ddab429aaebe1543bc611e1545b94a7767a6ce8e2124943bf9491e3c1b68e5a6bb5b5d65a6bad56abd5da6add87355b6badb6bae627ad76978daaa96e55ab5656e7ea26aebe71d53429a5c621b9d3e39352ba46dd87e725bdb192973ccba8a6759a56f28d4392d1aa694e69c937ea5b49dbdc876b9639dd34adbbddfa9274cd6a26ad6a9ae6acb965136bdd5522ab76d2ba9b82255749f553d54ad4dbb352f76877e7f55facb576c65a6badb576c664edcc8c9d711f36cd586bed8c5b3799baaba42f1b59197bb2263b63ad7519eb27699223e909f0ef8a1c5c5e0e2b0f7bf877258e299f99347fed36ec8ecdce944dcfb2e99934714870f8c9bd2c9393fa94ae84ff2addf8586d817b8a9965136613661366134e3fbd8799cdb2d36942eee2f048e0e699fbf0c964ea30bfd14d332693499efc2a91267f1d4d3ed35dfad1144d5d2c617e4d973c6d7e4d6e9dc6345dfaa67dd6dd12d65d3e27d753402134978c96a912a7d3e9743a9d4ea7d3e9743a9d4ea7d3e9743a9d4ea7d3e974f25346537d33d9d963d6d8c9c33469b669f28d65ba93759a196bdaaa96754ae497ba8b69b413633588034a5f9e75c7d104090c929747c0bf2b4842d16432994c2693a93f9a2eeadd6fea645d61c953cffebeeefa9ca6e7a669324d93699abcad3c37ddf6c821913fa1c9e473c6e9518ba9a3a1a95f933f7f2693c9441df38ba357e69869f3cb8b209925d3c99a783a4d75edd45d6a7bb8693a0f93d7984cdec364f2e935261aced4ddad532267aa4cd6dd25349a653ac79c74ce39e79c93ce39e7f45b1d9bd444bb9b79fbc9e7ec34db5d6aea6a7cc6a7d37094c6e9a45ec3bd9743bb8b9a34dc7b39331c95e1decb999bd63d4eab5a86c3b5c4c457129082e038faeb8aef36e4e028fa01fcbb02471538843e01ffaebcd1c63f4a1fa55dffe490d060c3537fff5a7a3fed2e7cd9ad77e32be10bbbd7dd6e51cfa676d587f21e7e698dbfa7e2e1d569fc6a8e4d93679b9ffc3deade2a54f75e0ef51ea7ee525ad3bd979375d73ef56bbbf7726677514f65fce43ca8673ee33cb8f7644edde3d4635377edf3d66d3ed8dab56b5d56e4222975ed517ad30e09f4e9d7cbee428f1d8d5c22c3f387ad522a6537d63d677777f76c4d9b736a736a3d33ad332deb6eb637aab36e767bf7d4dca75feba97587a467fbe5139b7376fbec29babbbbbbd9dd3d5beb9eb3a7d62d5bb6c4bac690b4d76894be8742bdf6ab314eebeed67a70d559e3b696d2d4b31bfaebcc3b24984fd73cf3acb5d7aa092157b5aa4d6dce39b55a6bdd5ac5aec1ae6a5c475155bad9c9c7e9607a68599d5ee70cd3300f36397777d06fbfccc93a775d6b911fcefd18428760b4ca82939b37ee873f94675973d7fe121b66c3d910a9c421d91c87e7c6f16ee8a5f6129c381d92ccb7124492f9d6ce38ce977f739c0e827988f20e6edc659799999925b041ef0e763638ee016087dd6d811d3a74585f60871396e055c23f38ad1669873c03e03e600b603633333373779574a916694752ea66bb4b0104a06be89356566527997c9ceb71b8f71984190799bbf697d8d00c8d1a22fd701c1e72c9d908348229303c82fdc34ff00bc1f855f2f6ee4230df7d67287548a0fff0f621367ee78ddfeb4df02b01e02dfdf17d014aecbfa7734f7876ee09619c1b9ba7c3eee30979923b82fac51d81b92c7118b7023a1f053172474c2f711a770490afa65fdc1140a9d5ec22870225f6fbe9b0c327843d9b1ac6bd15e5de6a726fd5dcfb2b09ff877ae6f8621db6f9a76341d8f6d97174098b2161dfd3b9f19642763aa7f848e7f0c30ec52926168561402b4094648c0c2965126c9729f0f90776c317696b36d0e49dc3298a3d61ef23c2a2b046dae6d26115165c802106211c6995ed5a657a43de5ff844e6d441b71d4539fcd1500d8d524152a5ce3658bd56569d597561c98fcd073b5d73f8756a5967ea6c767a5ad0e8e570d7a22d13fe65c19264d46fdb6e05fe721fa65cfbf4acb2ae527561c95fdc86740580eb742e5617963ce46e5e722f8b1d5e1ef6664a02cb0fd7e71d46a1552050e3d98d1a76475e1894510d36f83ce8b4a70805210aa260114a29ed9e9e9fe9047c517bf5312af63d1df8c0a85254546454f4050c331b750e918f19af8bde10139ebd8de20b9e0a0ab51adf713c7b27010584f5e1a30c9e1d7b624f3ee3b78b9e17c5232418fdab2c1b0a583f790cd64f56c66764dc87bb341bb5ca8773d85b462ba860f4ecb0eb2f7dd41d4617b5ae20a1558795cfa2e6f34f05d5782d9f2ccb20e7f374644a1a45825b47525a75a2714aaded2278d445f0a88b7aba6888526bb94529b75a3c546d6238f94c67039578baf0288693d37436d8d0b194cea148d8da258cbeae727213fb89b3e1878dfac977d0a5d71a3df3d0d391626f170d09d51e1a1a82476fd439dc4527a7711aeec4bdceb1a18bfa6f17e9a0f5fec2961290086a619eee6ca2a3dc3617bb68c64fae030fa7711f7ed58602337e721966ba1ba8b4df607dc64fdd6b19787437c8f8c9693a1b38c53614b03ee331d899ce26fa5b59ef2e0699aeb3893e8f5627c76040af863f270b8fbae8e5f0d191e95f962d4af8ee8a8eea0ea7d8bba855f08773d8816890854f9a4b177551958d7d10913d9c0ba5c029d0095b4369a65441c20735508a9437a40a74020eb5aae4fc886097e757049fb40a42a18129c01f9ce4a0c689147628c53a0c474a90a406e641800f0bad7aab2952a44c619ed9c52745ca0ff6637302878dfabd7e0ff551e35484df11763f790fee04c1a1497574797192867ed97134748c9402416842261008be2008e63091ec4f8c1c234bc8a4e10f13d9dd1d84c2e0e75d578df1822ec4a6dddddd32bacf227e7c54e022a5091e1daf0dd9cde0103a74e8fcd1670a1f15b8641a9472cec77585544f909412f8ed36d0337f2b1bd8d95060f3cc65d83ceb6ea01243e6d565a8d97dad08f84c677f4091c7235f1753c608b9f797d61cbc64f7369db38141739b0abc95e63632749855e619ac11800f8138c54c72b03030d9bc56ce877236d4abbf95c6d9d0ce8602996f2e43e6dbd6c1d01de91cf61b3a2c077ba90fc6181854cba21623579674ae45f8b3a86545b47381532c40b1ef530978a9023c218f47a652485db1b224ec6d52da4d9bf6d371e1511d9e110e807948cc8fcb43cfdef3610863dcbad7b5e39cbfaf08bac02908c44be13d2137e7e1bbfc84c89447214a011efae5209ea1746cee2df5742412526c5dbc9581423dd40d4ea73a08a52f87a3a6e72b098ba24ebc21d1e53f2c6be4f0a8a7a384bd9cc2086144a11ed28505ccd2c3932c3108cad2431043de20ac15f83100d2aae4368f7eb19e250b1a310df886c0eba2034c18311ab0a1c7d00af35257c7942b7430b952479412c7b5026f8575a8cee12fae862a6ce972755be4c0d880304218afc9a53146463800c6078082562e29df8b549e404d19c0b3c329b035a5674f096af8010a7a22b483829123af0bdd29ed6036c22994c256abd56ad52253e014d8fa56ab15e4a1f317192979117a9472bcba927843783cb374eed9b1d3f3f1ec2b221684b0c81e84cc1d35c9f3cdbe227d7327087318d598bb5a7346a97f6b6178611856cab24cd3b45aebb669d69eb4d3c96ab56e9b49b39a3d31c7dc4dbdaa454dd39a35ad5302b75e50505050505010d2ce4e2ae58e42c5d85aec4d7dd0ce4e2a15d43188c2ee3a74f286bc20f8e40d01e2a92052113fe66b783a2c804063fe05cf6ee406f642a4a0eec12275978241d007209e7d16f1ec128967bf82f809449a48fc44f2930b899f482424d4c3a0206e69466410c6eed80eb90834951b6c31e68f98feaa06eccfcf0f95762a317236718a6d876e73f99297b48d96cf63c147052eec8ee4da150fc2e77de1bbb73f87ddbb721405be8c51c6f8745a3e4634075d8e4a90a404338481f5e15351db23a3ad82526ca1d0cab3df1e5707adcc615fe7b8950746aea4901458458512bc55abf58c8edcfdbae89c97d3396590a312746955313a5282232368c30747517258e624b2839e082100fef4b76a6c078b42a18274771b1da932a1a452bc930a8242fd2b099b7a436e9edd859c98ba0bb8efca73bf97bd9967c3f1ca8285081fbb6b53cc0ceb0af0af39af54ea0b4adfb354a3e178459db0f70d89cfde5efa77254c949fdc5b418eff8af18af1e9a43845a910ec21f2b438f3a080061c7ebefd3d1c5665fefc06f92c638dfa95c41bd2cf30879d1659d813e506cdb3cdab943176f36752cb388da301879f8f1d114e5d46589f56dd40e586cc35bfa1fae6ecb1b26ca0c7a079e6316c5ebdab0dec28a079e632c8b0753e9dc35ebb54e734616f2aa5f95bb1efd8c090f9fb183acc4aebb26e8687f24c68d354ce37616f9837f7bea70763ec8b0c4a4a4a4a121399d8ac50bf5dcb16226b6aae899e8eedba4d13c292b353292b0bc32c979f679e16c3298e405e9ae80dc19c3d6b2b9e8a99e02a4d444459cbb3535a59dce2d4f5b7899a882661594ae7b0d3272c5684854ffe72157a848d685893bfd5ed23a2a336903a4c272525111111d12a96fd8219d62a755c258c2df9854597ab308f185377e393c7dcd48247493fad924033087a02061ab5eaadda3718b120d8db49510af3943a9bcd5f9cf286bc1274ce935659677a845d46581e6a55fca99d64ea6ca0ed6ca03f302bebf71125f1432b48b0ed1702b1d039ec945a0bad0995612f4b96a487493b954321e9e5b07315ae1293b84a27611d57793af12f57e9d1c1de4eeaa44e628188e8f1631886519a9ea424195479a68eece16416a22c0d8c4bd86b6d74769b37f37a3c9e591f85689939463a316f0d3e9db137ac54c2b8d4d309b2e302a7a05bcb970d6060c10524a424480e9d7396495ae5ee7efd754aa7e6dc5329000547e1d95f1cb6d98dbca397643451987f51c07e60ab63522cfcc983656173f68f4976603567582580b9d62149f2b4a0c1cf57af9d4d4d52cabc7254e33416b2aed4a1e02362c1c6ee9230bccef9185e16412bdb394111c381f561161ed1236241e32d889f2223478c70000c33a4b0550bb8c2a61e38ec45151db992b01d6c8fec6e8fedee6ba15028542c32f273c901809662647484e848ab344d7b435412e0a2693108a54f8374984ad4539b637e5f2be38dc376b047ac11edca921f6b91ea869d7fb518e4ca8625ab87d125b4446989ba0f6334259d88adeeeeee26ed0409090949436a1511229008741fcea6c3cac23058a77365f19873c608358859614b56d8ec2f9137f9acaccd6765592742e3afb266ba0936c546efa0eadc9e55772efe58f8d56f0335e1147b951c12184b5dea6c64e8e1355e811edd0ae04bcfbcc697bc0f57975ced204e715dc3c1f0021b856c4e355e4f42578d4251280a6145d84e7af286bc1f9ef2ec0dd42da5db0bab20176ea2b7eae1ecdde5e4ec1dd44f1a4a0b759430a8362c98bffda5abf0e8fae7e9fc708a9d8663269c629fe118a852805710890e1bfdc22e7c21177e39453d3854e734d049cb4a2e6b6e2cd74f1a0aa7d837ae85a6cfca9a3e2b0b73226f087b81b502bc6aa0ce61c76e605fe7d8a4563b4810a9555dbb4bb5ee528b924833ffb268c1c177d740b2542a65a552562a3d8de6e9f4345003c5160dcfb08194a486244ac4a8eae657fbe650a0843e3fe49a43417d25b5c80ae877a7444a8901616dbafae63154df3a1ba8b4db50690cd537df3a1b6ab76485d1c0de1d9bf6eacfa63b1b0a6c6e721934afdd0dd5353775375089a1ba66f21b34df3a9bebadbc342f13b771b30c9ba4c4181533f8ae5c0d9dc3ddd5244a58f8fcaf73647769ce0e843b7007f6bcae795d48494a8c59c15d204c4aea1ca28cc81b529360d21bc23d431abfa993df1d98d42a9bcc7965035ccdcc70b4731863c2dee730304fe6ec3118611e8c873a3becf2865c4ed3d91c51e370c5c3d9e113211bbb42b5cc85405f582d58b07f578ed890e13c7238becd1edc1158a5553d3a1f7e53a82e3ebaf7208ac7d0b3d770a9ce61e7c1ed740ecd329bd42a224c892411793a4ce40d610a87bdfe1279f6ae2c1b1868bc026f45d3c97025e9e895f45fdd7b3a97cf74f30beb7f61114c8245b5bb36a9a8fecb92e587a4a9695aa6cca8148b988a6335ab149b9901002008731500303014100845e3f144d103d907140010809452564ea0c8c36914e330668c31c6100400000064446648d30c972b52863a40e8aee7180e1ec30ac0fa2170c12a096bdd78cd35754dd4fa5a802cd77cf05ac070d35e1faf670b6d272bdc7b567faac4f6c6112a8b9be07579194522cc1660181a8f0cee018a783d4bd928f3a611d2b5985974d59f3ebc89582ba05fb5e0bbd281b2a55335429af011bbb70310998bcbbefeefcd45659fec19f875b54031136e9a24d7c5b1a78c5535467fffb62dafdeceaec977a26c53220b82de374e81baa7740521c0cd6e704ab2c6b0d6b5f0227affd64bd982ce11e4088d7a6b13f8dddac0c5cb17e67a3277ab25195bdf1515decdc4f9a9511707e6539aa1ab3010e22fb5025cd40566ad621eafa3de41ce8618a565a2a7cb7c739fba732221f711cc5d49b5f6aa7e04384a0ef4f3face23cd575a47466dda28dfcc9993520ba50e13fae95dceb02e45e3a1171e0644d1c746e78e91970fd3932f32b469e8d2dc8aea9ae3dee0880654a103098e29a9b4b0e85dd175cff93b58e35fe38ad0b4253b089756da0ff965419d851abf99c266bd8b447cbf472034b8c756ae005990c476331a4c9d04444b4e6254991879e3ac96131c9f9c7dc34193e28f32b1dfb1f6783d1629e127c9bcf98873b465a1b6a920d6ca1ff90d4ebca9240c61f2b392e920c6d8abb43289ca0d18469bf3942344a82cc52805cbe37aaf27c6e4683088e20752080a541145b33e3e360427cc102a68f9eaf638f830627c25598c1dfdb1f4a4da40029cd91e84ee80f351fb90f7ba52eec4c61325e9ed038f8a514266285e93ac3cdff515461825f8f67d8d67b4cf591081fd7bd33463fff1ddb3dab3da984ed36f05a72e71a46e915443906b12456c7756a934b19d1eb5bd4d6f7d87c3c6d73237d3d5f49fa9222520483c4ab274a8947e7f0c5635b67be256d9540a220a580110ec7a0d51564c2fd715994c02a7884af95a17da8967b8d938cd22ce9a45290233c96b52cb4c7c24fba860f8fd38e6c17590b8d0cc65eaf0507b042434aa10906cb4b3241ef0a92c21f7cb593ed25c8511a23dc99929d778c7f58dcb3135c133330c777c8fdde146ae2df8c21fa74181b9c6bf24953dcbd2b02373343f8854b13a2bea6a22d86705a9c792f223f104f09b6e1c07a4ddd8ca77a7ff11355e7bf8e55d89aa8483a40872c69d1c33325ab25eaa65b6cca5dbff70b3c8ee2a17486824f0c995ec58c569262b819486da1dc2bb1a910998a005a50093fe2459fecc263091f9a8a9d6fb8477d9454903358881347849b0e7b9e1636dc2afeae4b6c09bc30aa9ff1d8849bc2a6071bc258ac446578d69ed9bb4dc63360fde03741c817c67b45b7f13931cc6fe3f1a70159812dad006724c15983f8199d8609a6d04168c2dbb7fe62349553d5a88de052ea91ebf389bbe223e53070a7332cb5f932d123a2b37a39d56f0feba9b60cd122e8ab4bee48d7a940eb24439b1231290fdd39a27e0cbb00d60de15d10d63d08136d75de155faa3e6908c8ec7bfd201596b865ac5ed1e44d885acabb9ec06f4ad14525cd4770ef41ff8c4ffe915f2c23a4d70c3d78803171c8e3fcc7efb634660e91f7df7c197c7a772d79ddb05a433f9640c292d9ae6d166ac18e2f8f68e054f05c08d6b5ff7d95dbeb9b1cd8f2e04c59b685a68412cd3314f7a8958a1fba4158162a33d50f9e39bf02d62a76695a4d9d6900987547ce551674d4bed6ecc56f06b19b6f209c8e02ce77ad5b43cab7106883a74347bb52aa503242eb564a8237ad94b34e2c516fd6281b79d6593870b6da2d7ccc13b7b86f501d8d09f3f158688bff553c149e46247e4d1955711fb47e48188de84f5e1c6436cc3c84dc79a79a00b7ab57171f08998d1c4fb7945a9367ded8104a07087b27b4197964d36f167804737f1d725e470689c0570f07d4803956e49a945f8e72723f0a03b91c9f7ec449e89eadf7a11ed6ae8d2477f3bbae4783b5f777aac3cb6934cff89aa530a664cab5517938ef4e0bd19382d5147001102038af21540d6b9b56870a299dd16eed6c74b9a86e186ed89693b155e3517621c9f80443e11dfa753351b6af9972239b15abbea15196c78d0b55464ffe96b5886d66b31573d55eaa15928437bbee26d7db02a1d932b8e556d94753f2422704376c37aa14924ced73117481f9aac1affd2b53a608ca90cde9b11a6a9dc595a76a64a4c676a62008c6f3b87f59ca0c9d47587f5b23457abbd127be0e975cf0629b0c068c3218963269214d4674c4f00de323c77c4f5b0ecf81520b88a1f168aa88edd96970aceb685381dc17e075c1b01ef710f3fa2dc3e7c27dfaa7a0a8736895c270fcd1f360e62231e6263ce51a180af564e7c7b3210118522079070ae7541596607d93b035ba790c7c2b2c79d70e5fe069b3d2e24bdebb79360fe0a313f50068acd942e86f592012f00b5d55204bf0a2dd3e0742f141eaf4cfa7c252b97f1f3a360fa2a45123895c97b99947ed19146327956c79ad744f35a1c06a6a3eb5a23cf50280bf28a6769a893938b421474c64cbda01ee7c46efd36d0396ba5077b8649e22e52f26ab98599ce1a958bf0cef5dcbfad8ddebe5682fbae1c6ddc17f7bc1af1c61b8cd98f2ceaafb24235373bba863810ea4c56aa28b8f4aa0e849905fe0fb5d98f3d0832c4630c93b7e198602308156aee5c137b4a382571c73637eebf792e47adcf9e6cdbba519bb35cc027b3a9079e67a34bdee8859e3ae7c3723960458c1fe9493aecc442f4a7efa73dd94675e3dcea4f259b0240eb6d27272ab17f9af336f75faf040432e0e2cf6b0039a86d79db2efe10f10ddd0d27714c184ec6174efedfbe423270623a0ca7e09aadfdc0bf8ea231b1cbb1c2cfedf16dee2110a80a1cd383d24078b2d3c91f939f89d247cfe4ab9eeb838eb6d4a94b54d48e604a8ad63fe54e56f34dbb5527232e07dc9b0ba7a3ce25954b3351487a6e46a57d56f80b5f197760a35638616188545e771ad29baac9bf0369c7ed480d2aa3f9140803360cf9cbfa30b40f5643e01a088610bda137d61b5bf08614bd4f3ef2fee86df4577e47adceb15bd6ede5caffeda975c0cd77e046ec5ab3a81445cd88dbd3dbac1e4ac400aa0377abd4ec67806a7c0e0984c9c10baa2728370910c4e360863895e5aecc0bc27ceebdccf810fbae3b4c90c1d49d9a82b5693073d5916fda30d282c3e247476839adb1a4ffef5b2d02b027fcd959c0f06b8363c930f90f813a1b6983fbe412f6500a3f38fb25b04538932066b88c1e12020ee4363e659635f2e7c8661fbcbb5b4b854761fee01d1a74c9947fffc53d332e55ca45852479b318e83af67cbf76d71104f87fbb277920dc390d40bff3821b1406669f7a759a099f2fb8d55fbf741bfd58b1710e23e706a9397ac744fe115c2959ebe9ad40a2dfc75508d292833827f7697b495682ab90afdbe2af0bdbc6cb7a2182be509eb020f286acccdfb1f418d0814e841c1665c0c34d1431f7a17c9c9f801bcc8077738b19179e1b680de0c7c5b4c6031e19e86fbce1bc40a0ec99f97db4fb5af5f867b772514803430393c70e2ff25a1bd7fb33cd4f978e2654c20b84df67a54bdb63a4437319e4fb545fff42574846414d4781c4d9311183b9f908ec388d8a89e4571c25bdb54719439909b632783e7b6302eae07018feccdc14867f4070a750cfe6fcc7e9147a6754b89a17b139f2e74fbabf80b80e403cc1fd3dd4956e9cee2eb096ae6cdb7602a7f79e112b6ec75bf9f556df6e35648cb70a5510ef609b4a60c574377cc595c7034e3bee6892c9072866f7430ac5c3f42bc6c62524fbc5bad8c1e9e0f5622fed18bac41a1b4ecd6d95eda34574e563520d3dbadee5f277c1b77a1759efb4f78c46ab5e25f6da15d24785b3e7a1935f34fb9eb3e4e631e038a65e240c347e397cd69effa2b137b7e5176afadd1675741ae52f57cb61cd817c3d3f14129fd38d20b5a5985cc989463d17fa45109466c04fa59337904962a23de5d11920154f79a820265882631dca2b0f1586f0377cb13174959d133dda52f489b6394b2ca2ceeb3e20011bb81cd7f78519d69301170ab59eb35a3c4d025ffc6b4d068142d0a85ca644c58583881178957f41440486aec5d83c2bcd92a0aa1600f5389043b6458e12fa48d43acde917f191975661b13da5ce41dc800dae3a87516a7a2aa8b3a7159c80ab657e4f421b49fe186ed7a00dbb1a4cdb42ee522b5574392861ee5d07897c84157a54d3d2694c77fdee10720ec7eace86bf413618bde6dd0b7b1aa1b53724cc804375a69d2ea7e1cee723f7412c36d34123bb05d3deb8ed2116f4de763b0f8cef7db41f36b128715151e44cdb9bbdc6d5030584faab265681151f0615090661f60d0cfc3d40880a77ad0381d01934e69de4834d2942ffa6164f815734a84036c805a92121a336b7fb003782176ffe623db50971fa36b3750afcf8f040bf591d0296e0f512dd3ac11f0fab6ae80dc6c04926fb5710afa99bcfca52bda7cef9cd14c40ce2e32fdf21dac435208d8414cb85708239bbf5674ee50f73a33315ab8fe3591176de9b4a33a42dcc8377e493c2f66224509ee226efbfaf997f9d25f14e1bd17542ac30beed259f6a3e56e96ec603188f0d22a94b657cb113ddac89cbf1383217bdf8006fb7506f30682dcbb3df6b61a794d6460d47a5ac93dd7d995dbb92fa522173711cbed8d9c803f39f1abddf101730df1eeaeddb941b301d5b5390c3da45732ffb97cbca08c0e19101f5ae7f2ea8dd6a5539ffee7cd02e04ef4ea37f23cddb43b866fb7dde292609e5e4af03e0a8d664593668c26c3a02af71ef17ed0eae9ed38ce6adc8e728407d18e4b7f1ec8c6f2abe9a9b2be67b6ea2fdb86d2647e3c5845915788c3c87d12a21a0314f89f52bb0c749ad7bd8b2dbccd0de2e212161d6f681c1a54a76381e1726b6869976da64a5aa1ea61fc9b60c59fe0e99c24dc79165d36a58260a90c6e0eb4c226461e61f9c99a99f5ff9cd2836192532276ac2acfaf74e107c26639f02e27f9dc425b3a614bb7dd083d996a433e8f0b8c04c83c8da54d01dcc28145818c2d18ca3e34fc423e3de34599e9e1dae6006daf4a8629adabbb21dedbcbaf55e8acde91a173805b3ee3ec569230830bb8e213010879ca1fbef0516457439bbdfad88610c35931256487878b3101fb049d320e43f227a485599e8f088ca731c62f8107e3aabdc5f0c6dda7da577cb642d119f0e861da19e58e148122595eeb145f5f38cc58139af1e18fb1045b92e12c9e1770210331ed4e706b4a9a60c7ea83d0d3a470b8e4cc6640e4909448d105c29b4a6f9a67e3b0003a5cca2d4c3e37556117214de278c94ba1c47647cb1b85e90144722ad9d0fa3cd28b4358848d7f09a94e694aab66c5b00ab85fb4b062df2faa99d373eeaee309f696308b0297acf5d79c5c79c0990d525455dfab23f38bbaa9e8838c4d8972df0bebb6ece2f0f5f55ff0da3fc03da1f97c332e3b0e8e0c1ba759babb651f25c51113c488a808ab13651f7ef6c267701da64684fdd03adfe53829ef5e72315f14d6633827db15cd0720a080b3639689893b32da726ad580afd91451d3ab327d1deea63ead93f0c0f013f6e611dde5b1b631d1180748b9423a16e0710b4a08bd49139e3ab5c003eec22f9bf6d860c4b9ae83ed6af0039f0726326c5e188fd0c8950d4e1565f6cecba9f419c0743c0450e19243a261d5205e9d04ad2b893c660f65274006c77d06eeabf8b6168c377cf88ef9cd3cbb8fd8f31cd25974eba2f309edefd6d59af94aca97c9fa2c09e5ffb40547c939bf69cb6a4302c3194168b90319912689e0649a74c0d4e20e9b818152d05e47472704b4962c849bdb57ec9533467ad5e2028dc35a48c2f8e185c39c366e500a7adb85c6b11bc085bcce419f08beb79e33e02dcea729326b0b83ec8d168cf6abb616489eb65ef6e62a9c7a493d038fda8f65aa62c368cb322a98a068e8a905b9bb84fd0e75bab4107f44b249a05fbdbf905621b7d22886ce7059d080a99026b0c10ad605d41ee71e29eea20c6d6d252f1693e90eb9dcfb463af4e9a19c9305c81b39d6f89891cabd047023d5be67dbbfdbcf6da0477627dd3eba6903124ff00641b1a46f13f7c045533ca7706b94545d1959de4cca8602b14cb10b5338e38598824c3a92a082a362ad44b17a67e8b6ad6ab80778c3a539107fbe1f986d020150ba361aa134e64f124894d003744d635030d3026ff878f601334516f0b6b7fd6e1ff5fe94f107fb3a5726c7744f32aedfe279acd4434154c4cd8e358cd442f2ed96f95f12678e25f03eaa7bbc077f5436516c1c7e2281b06b177c9dc296094a61749af4239169c89acb6ed4f980cca977c20967a8bdf2bac7905585594a1fc12a2333c6507efb098af83a99e6f8135845bb67bce4786e0eaf830be11b445d7cfdb8e40887cea1a0851a585fec05bafdc1cf7adf09c0561806e4745ba98e869a8230ad1cba3a59727620ac0addff991a8c1e4dea69728c8470694ca25c8631e5885e2a770a6cadc23172fd46b03bad5b3048ae802d8696d056eb437cfdcc675c4b8ad96d52a233f35f9e8ab53e4e41db6a79874fd73c207a04bd8911a7413ad899dcee74c3a9e300a4acee1c6925fba3c5cd3b38d432d175c6771733fc95b02509b07e94fefc7d8c9421cdb83652784f77c348e2c17c394c3c51cc7508b7edb23604bafab4ca53d2e0c1dc35c5df7c762ad3d366494475916ed599407b7ed803e999b202863a211e8762d3e3d7f655c6a6e543a33e98c0e4560d86f1b47d382066b24187c92ea5a8563fca4f631fb14554dc7ab49e8b93382ca7609a1ec56984711c91c20894b479a09accf37a2bbe5d69cb74cb8250c427bb0b0e88608863e2de497793c328297424bc9b2860572d640888a4e8bc8b44c7d0a9a5ccbf8f7828527716065e366bae83d8b9128731435f96d479e2ca8ea497265021d4ad6975a304ab7fce408bce0f01ecef30b966f780f10202f1f73b60141196a214af3c4d2aed1e9f3b9e6f6cd83cd082c77eb2d046c79b3835bda09a96b7b4150f53b950a962c019d4092a258474ec273baa4e55398bdc95c8a1c1bfda70e1047bbd5fbf0ae2b9d1ce46b369e8138c018491ccaf9fefb256551eaeee6581989cbb67f0e3414d3a2679e4758e87e8e7bf00725c1beebb38aceec22ef81d135864185ffaeb2d02a05f4c3aa3a92c8449af74f8654a778ff489cfec01baffa0d95212c3683e747998b8ddd536448a18a51438ec2a412881616ec8b3b216bde8834aab01c2e6a5f3131bcac6cd4bb7908208a2e1c63dd0521b13c460dea27956263fb2cf058e4352b32a81584bb1366c8389bbc2b76000db200eb4b4d407c875bb771d3651fa3c86344c43280d297a2fcd8c51be8707bb1c177a7988acaf689f88010c5f3379e81cba2b885d2ce1cbbdfb6e52645cedd8c74fd52fedd5533340aea0c65c72ded6b5a5d77948a1ba3867fec2970e4b361034f9238f69dbf467e23389662dca34e1ece86e8dcf50dc3c0ab756e1f05b143a499285f662ecd3dff818687a6d1adfe9e3c74471a35652654551c6705366a83d7dc969d86dd3227901f759e2ec36ad882bf84539c779a7e223ead215dfa9550818082f4fe34df9155c7f31453ffc5b84306d6e3e5726f18555510a7b44bc14c25589afce41007446beec9ecb8e9f74bf5c446bde3855ef439638bf7fcdcc8539071e1cdeb001397e834b1383deb30631a4e35026b54fc7109f3772d1007b02288938a9d66ed272eaa37fc66d2dd1b0bf69f5adb5a2cb61bd13c12c06c153cc0c859ada8fb1ac924da78e355a81b4d79a392b5eebd6ec81c6600535c070738481154f106a3f9f37524d1801c90873f633e1911c9c47d71d5567ab3ac0c942f29318598b50c44dcf7981e2931398489c72bd775ddfcd0443f30f9df4d43d05e41ee65793f88e5ef5ad73d44b8beb6a5ccfecfb2d2828aca74bd729192cfe28f98d69d8d3ae1b98aa757c1d77018c9a17d21e7dccfcb2d36422eebc82d59e119b5bbb1c0f710d61dad7c43c3c43e2aba6fc45019ebb8a806b9bffa6188a785e5fc0aeaaf18844a467196699e35c5bb4cd4c5602ef6282eb160968c3b6eebe27604a60a02b0ece2ef42e93fcce2c0ede75e36061ee4fe67daf8cabbc8feea402862ebb3361a349f653fb4f5bca3351a17056082641d2b89601146b254d4309de6aed100add457849573d964d40d93a76ad41b8c7e7fc794c3b4b888ac9747700724ff3e87e9616772964723a023ef04a1af61d054eff8c052cc8ca913397045d33a529d06b1de59f60bc9d0d580b19df54a6536dc3fb044d46db0c4ee0b810aebec7dfe154ba92a0048e000720ea3eb6fb51390f3b6baac58d10aacd91a037441ee9114927650a7eb91e5c0607e4a82d8dd24eb209a0e2279ebad2e5f96c31f0c81d8b3ac9dbeaa1c79c1f7a2a52f528e96cdc2c6a684e2d641c75481ef9bc90cd016db8d0d1cb2cb7014861da9579113666474aaf6209dce2aee69a2bb7412eb0a23106f9c5bb874d9fb6ec41e8e6baf6b6957bce19db175beac1ab2f892f756aebce19ab7f4ba9f6ec972439cf7c6afdee6621ff2bf5fb4ead7a003b3219871adbdc8bcb9619cd5595184142fb254e7a26779c354a29e0d6cd020e73fc06188654ba35dc88c26362e53a59d2ebdfeacdc77fb401614505dada77f37cd0c50a8b9fb8f77269b3b7dc36f0056a633fa74e0fc7ac7d33b3ce269dd396ff8f2b27a16ee007a08cfff135d28572b59d88294f8c3512739f9ebd72bcc2c1da75c64ef24066406a2da3a4ad8ecd1bcdd33c89515136d466833f1b5473bebdb7eebddacf6865b45fb9bd441c42e69d38bf420eb09041a68e0da4449841a9b1c8683178b2d02be1009c1a65a93abb20cf57cbf5dbbe827787909dd66665aca1decc18d7eee1dd75bdfd5f4185c9ece5615c664ba75618ba96586d9b194b3ad11f0c4d7c6e03a199a41d58908d0df6bfc52419a75605ff558e292b821c5f0f3113496ca84da07016bf483f9d4c4ede1769a7c9339dac447768ce46ce5dfb45b0df40088fdca54566524704a210103e15c90505ac7c967af194efc0ab290ae65c17e01938b8b409eb580a8049f1514f1901e00a33d21cb8fd6d7ba09cf9986281c8e388cb731f39a1342513469dcbf755a008aad25cde2f289cc401d37517648e11f2154ddd959440e7fc70daa584ca91848fe6e21afd3694f3ad5cdacb3e57a38f731ef40f79e5eae7329b7975fc9811ef6fc41bfa7d47566400f61eb2f9dfe0fabaaff913edfcdffec1d47c9ef9b2384e6b2f106c21819737012574118c22a5ca61ddf745c3433534f431d047aeb25ebc736ab1f18e28f98d8b3f17a46b3eeeba4b9b454f9cbc8bf1061aa879fb1fd8eeec561a8c889879fb276b13ce142dc3026557da243b7b8c0f59e2c507a741dc4b3cfe2a14617a516aaa2394f61b31f3a3795996f6970021c8cae31a787e70b9309a5124d9dd5d1391d1cd428d8469fe726f37624588b18f6c0b4bcf2488b553082271114d6f31d77d6f1805b94126796d82e33de84f7b6c5dcc97a70502950618cf2d353062e28710c7d7c54f893a6d03e5c637ab4140b9aa6be782672fda642029dd9ce04138d7d520c1f6991dcc53943caf608f0df1d912bbc8e4e303be0d8680d20cf54ba3f861310335620ff00353e2c09d4accaa7b5cc6b316e7571bed8ad63a145788238832cb2f935083c30c88943d6c515f3a6651462acc8f14cf2fb1208e5ea5f73ce977c0d602b250af506a64f2b206d395a234bc384b8e283da8f24f06447cfd11696a8d830a8d722a4585bb5198f1021cf78b44cbad7bc8b5c67f8762af99e35e9d02a623bf4cd8bc044db723e4bd71e4b7d8787090c8015afb1102ad8983977a004e1c2ab165a224cafe61a380fcca3e99025511fb62a50f03c8591970bb76c85817c5e474faf19bdcd4a87ad8f24800f2fe9fdfdb969cce20c46c11648a39b80f21968c6eb9fc31444e5ecc6cadf100988702064eebc915790e0b2f299ba2b2d1b8909508b7492df7451c1b34f15b54f28a7456d7a703dc228ce9994192d5971a51b503d78448c7152645f942b2c24333c3936af93d80b19b8da49b9ce51b8370ca1090dddf0a6d54fd928eb8bdc44a6f67db8ad1876e61226183567c9923a0f733e3ad5c14385917ad72bb62f3474a7a0279a2d4596a6031ae10dd944281b4955b63a19d7cb359a39eed2f969c13b60edf75e247af817ba51273d5ffcadcf1ea4d05592eb7da6b9ed1d49f04f903522b6d1dadf23d0bcfc8e8b8e3c67aa30916390c2452f23f1eb6482fe04eb47a60c08281ac8f6626414f37487b65892b738dd95225fa7f491a22578763ab8f93b94da856f34a6478e9bf39899e2f570949ff408492820cc3b1cf13ca3d335faef7dbcfba0a61eafd094e188269d565a6fea47dc27f4f4d1c980ce7af9d8fa41f81c56f7c596f78dc7afe25d6531bc18e7bc96dfbdbaa6311d84ce80f4e7c30050c7c86e435c97b7629af7c2237082ce7aae2a743afcaf186b62e0e29697d7c0e03fed568547c49a00da7f048eedcb4013d0c90c9abca7588ae01dbc988558177ca9a5169d48410c9750a656fc4333bd011592ded382bd782e6d11224a2fa50886980783eec0f5d5e0dae17acd8c2b73fb7c89c2c27869d9abfa9c621b2ee59cbd7fd84999bfb2327c39ceca92eea42c3ef68a1022100d80362673c2fca29aed4d56bc262a6b462973e1f52b0c25a48119434d7b17b8a00cd6b0f5ddb5029a694b7a35427f2621ae9f092a3cf99234aee77a679edf96e9a49f3f5d605950d5fbd1832ab395e4c2ac44424f05dfee0b161d49b197eeab8072c641a197fbea84be06f2f13cd529d258d6b89b8307ce8e1edc28f9945489bbacee866748198a5b33eca0be8b8c29f32a1ba39776e7336b14caa7031380f64e20ac147d096493938eb3201e6f05bec5fcf19348f110ad8ff9252cd5068dfd56d9afb140209a6f2a6e37eb1b200a59dea6a4d84bfb71f9505abe420acb4f5b0132558abd80f436bf72f38bd5b31e3024a3c966fb5f0287cdf7df7c674f9b1228e8f38c51cfa933599a1096bb7f8c534277841c645204c7074aa8890bb6841668fa1bcbe384de7cd1684c5fe2078d6b9839613960f76626581e9f3fa59ef8a0606cc4a2a6465045bb590573f6040cc313d9c16e328ae74bb467c38f2784e3ae8d605995c73f2474630d51e5f543eb22b4ca4fe80ae5f799cc084282a5fb37cab47a06af3e3783013156d5a3af398ab892329979516568573b25ec6ade3a118b218a72e62e7c824ad89c2cc7fdb8691e14fc3b4360c4932d7a4f69ab76161fb3cf1092781db52038aa8d8061d8530f4b547d5f651685fe01918864b395492beb2ddb7db559840425487aa8011f0011d64af1a45d51f80ea5c5b8cf468179963e22ab944fc67c24f73dc06d5b9021454effe532a9d1184b7a92bf8c7191b69dc5436dd78d826ebbdf7e6c94094d209e969f55bdaafdfdfc829d35ce266f760094f7f51672fa76f2ae00b7ca74d6afc656fb3031722a4ef94f2b0d436465a75ca7116b31701d06cc14c93196d94316cf22f03026a5a78b511e84e36a11e45a5f377b5b38f3e572de376d62fc3c99e3f4e8270bfa79610031ad2329922fbed2917b21782c154353f61084988dcd3ff524b82b2260d27e80f008c10b34467a865f410b58959cfe7aaeff888f34776594b8e8bbbe8454e27450fe1ae0e9982bafc100190c705a98aca2312fce2bcd2a32a88e98c5077162deda0a5474efa0e4554d0bff928468c33b62c930df6d3b2e9fc9d81972aaa588c53d207165aa2cd75f9ff897799905b932c20c8fc4cf19db2651e1bd4371806b17b7238b863630158f3e797515af4ee91c1749300f911515f0ae486d6f1904fb437a166460134317fa9f70e682b737ef43b88ddd8557c56f94f1653c7600c3a71ac562a6190b7a6194b64936904fdcd9a3cd4511241d13bb588036cca4b152f48c49826922aacaf06c7ac0be89a356fa8ff97c30d6fef9b4e7c33ebd5839a87ad8ac515c4531d630a91b9d0638179ffec90c40346511ab5a32a22f0487b5ed06b30f2ec21fcab42513623af5af8191b16043139c2baeb692b081f52291b8e289b995fb2261008955c016085ecb061086089acf54a6929c4fdee3e76cf5cdf7290417d247bcb1e329015daf6f7e6b8fed6b1dcc43c3d9c67e83f535be69a023d36014d795cbe1cb721ba01bb3d38333a0c0381115aadc68ab11b101511af51e7a7ec50a1f19a7a8273dd43a715a1e39403b42181b9f20bf2916279ef331d9ac156e51f8c02969ac9c10891410bae336b64595467d62142e5f4921066c8eb2df309836c4500d5076dbfe6720abc11291fee42691bb2a807b7fd42e6bdf76a780bae2592c0238506c034731f2f98f7f7389644b0e6aff3b912837377b603dce78e2deaf58e6a575a763fc2aa3a68a75a1bb18f78cc6e0a41d7c305c9589731f41f67a88f6420b776b654ad70ae4a8606e41f2d22a62d3902db9a85cba66fef3689ae7ff93eb90e0b5f35837ec6b672254b28a993930f8f695008561e1df1c6496785cfc9c3faf9560c6446193f24e16de292d0c02d6f3c6c8dc89e5cc415595f7ed43498682c2560245e8608d41a58b0e3c3a7a3a6885b4d484a6fccf64610fafccab5b959ab62c23e231fcedd0e2c99193e532af04cca930c94fc65cb5ab8e33f0bb2380d071e70801b106ecce85918c5d2c1860ce2c50095c1c314d4facdf2c7bb3238c5a15d1c6bf10cdb718a2862241b5598ca48a4d64b0ac43971b663d1b7fb7017db5c7950f3eb4b317d5bb79842dce2f5e52be3921b281fb814622b705e4d9fb5f640e7ae12d456ff485ceaab4f04e44cb6bbc24c6e393631de38d549754da4fe99d33f09a7cc0d62bfc5f8b6881af135ccdf151186fa703946e77126bea82407370017674e5a29482e89881d9eb7a3761a69646586070651cde0aa09472442c0a8313338c39e062585320db8290aa5e4b134c903a4a974457cf7c82b8ad000cdc020f1dcbdbebcc5255aad64987eaf47ef1d76cc0c33662b98e47eae9a8a6e1beaa318528ab45342a68e9f8ffa7a7894b1eba1395fc412409e6d1298562a076b8820b9e58868f59895cd2ae3a0e27a881f47e7b7c8695086b9d6b4908ed8fdc5aadef76f6138dcc1ff3e3e6a95318525d7bc8a565ce53598071606829149b2e965564e81d3134cb5ecb383005e68a6ab7140dd7e69b764a6b2cb3143470e7b13ba9dcc9ef9f28b7805b068ad20c5a93e86eb95a148d6dd231e8442bb9113a61d7899cfe9085b7b67e58e30a6fca8860a1f65f4bb1424fd009079001b6c83807721198b7723dcdde5095e8748a5bf7b9843cc8dd5e8c62f00efd2465529f52fcdfe381d00634e5dda3b0c03350b079126ef876012116b94c7ea6298d5f53b3a69a461b969580b84af83ee32b74d72f8528110ef977fac4ea0fa86f26ae7cf2f2352c192f77146d0385b0be0bd530f55722da98ba89e5a01ba4b733beb59a5721c05cf75d02f0af74e7ad6722263c3e2a51cc680b1bbe65905134011c638103188311405f0f5e7e6b3554dc90cd43098f32fb10c8243bb0f85dbd4c6b160017b393cf0cfd66c20564a1a4a86c41cca50c03f8376314a2d22064c8888a99cf8c320f03b7a881b3961baa8945cd844ceceaacba59406f930ff0213302e2b72c0d4d4daf8ac3867ba81c33a22c21c93a881a213d996602735b877e520f2957caa602cc31e7d14a360cae7bbae4a5b111057942641183eb93be43d8ff4982aad2e123ece19cd2888b7bc0371dcac3f004c018182985a152b1117dbfb032e2151675b80a453b7c7e65458953d99c8c9d054b10ee8b235fbb4cc401d4171408a2035fc4116efc69ce35009303e0a7a5b6c83f688d5967872d76655e2859e5e35cfbebbc1bb1401bb7c9df858fb77437c245e2854647dae49b00ec174175a69d7b297820040eada6630483510a1a72357727d9a379c109d621264d19b0dccd02ee13653dc9f4913723ccad718d7e883f71fe6222c40e32ca589cb047f66aaf74cf0a2842b64418930a8184e39548ec507610000301930b82195beba4952064202e60993f78f5838f3da0f4f624ca5eb56852efa5007a8ae6493a87008edfbd832f174723ac51390a2d14acf9108760ac07cab0c7d73547640109316a8b13b82b431fad6052611cc2d38b87ca7e9ac9b8c2885faea6298b4b03a396605bee9126be18524bd88892b323cf3a39172d64d6074195ed42708ec2d6e82435268c23614ad11450e09dc60220dc5ce476b5395967ff317c15a66fbac13195591c49a4e04209ecaf21b01005d1a2ae1bc132bdcbe6e29e6300e9ec5a03a75c5b26c7b039cb713a29a7e3bacfcee66368392d4ae243f7d3eb9db99eb78b273a974f8eaa17ca5979ebf2eaf1f9f03f15b1031b81008c14fc4a02c526ff1291087efea996d7f4cd76367865f3f44988508ed4e05c211f33d5f4443a93f6aa932419ba0678cabb89b852b79c25329501520517eac3e6b3c7e0d37e987b39688ce9c8b76d5534487d4911702b98236f8dd3799c2cda167a731eef646bfed80542cee5e69d641ede6cc272359c671ee650bd27f4dd2a45c5f05a9dd397019c8a43087e4e4dfcda85029daa8b8e3bc79d33ed45bf79955e749690ef3780918e989c6cc73f009819a7ea9651c877a6f3381ea50ca874fb5bd976e1bf4e56d6ff6989e2214279d229f0781733be80816afd6d40ea9be0f1a02178f257c9de43a812c6405b739ec8e1c17eb5491c2a996f4ed342316889d083447cb9a75d21f749e88904138ad43cc9c939a110db0c437e2bfcdd82f0621865c5cc01a770d04596c30a4a10c84324797f123192e7d25d8600d647a61f28b14821f1e691b002ae10bff77656e9327142f20e13ade725b15197f3950909188031e89131b54682e0d8fb9a849d39f0b97eef78e78170f0c98db5898c82fb46b4fca7c7293f591a6b0e743f638e2dfb57e3dedbb062e0b28ee37451f111b809ca054d2b89ddcd2a5b96a33d27942f673a083076c8cf8c85d20bc2d6622a120f37d8fa4a11b844a3c9422ca13a93cbb940e7b5fe086d43d79426cf966da764ee82eb2dbc80e4f3f2bd434379ded570f9c66f862b478badce759499c34bc05c6e7321a181e9c18be6cd0b240d4632ccd08dffad13ae6d5f1fd629cfab64937324253f55f28bc1362827e132c9bc1422ccb1a9cc1d761783d703f39b0615b942507c4ccd89bd74118171f03d42c6e1aec518252c8cc5387fb46b05b5e2938b38b96080e89b9061ec76e89b46f679c58aa0f579aa7b84a3faa5ea11ca75a97da474ae8632bc55bf808162e0c86e9b722b05ebbb4c96702591dbb9ec840b4cff87550005efbc9ff31a3f3d242e9e6d2ff763bf81dc3f25206fb0ffabe4555cc9756119f1290d5b2a48aa1af1cb5c67c3a56a7cfe6fe0f104440788d03f1ec59faf6c71fe85b283f56e060fda661b8be4bcdbc3134f225c5f730189e4a29d0d94c28eabef87a395a785f47d31e44793987d83eb1d491d01d1f8126516655f40aca82cb7743d6fc499283103fd247ed6b57276c924444d7fdbec1f56fce6dd802b967a05ccaee0859e0e4d6530a65b115f05fa354afb1c4b92c6f41f885f340686b64b4cf41156b4d550f927d9d242fb690ac13809103a4d30165309cce53c5cb1925753120065c537670a0059d0729799366592b927a09dadcdeeafbf2001f8bf5607af16125ec9987df039ff60b6926af7d9b80afb0e3eb9dd20617d2eaea5884ce316fde5131b6bcbdbf2b1856ac36183fb12f71a647ff0f9a9d2956dd620a670617574931bb812a1c4c3787811dbb79b0d47a29da7bd0452883ed0e75581133d7960d8da1f830dcf083ccce0b0ca12ce5b0f54a4985325811ba4f7d554579dce89d5afbb8a9421086f0a3d7b8545491773d5c0eb58179d32b34671b16e2ca4bea0738ef669f9229b5b34dc128a322eb7d6b3df4e26460518221c803514ad440b4b067469ed8a6f295bfa9125735017beac9dd3a694beca66e4ced3e941f4436f6e9ff0b20e4a2b443a832629f4f17cb6ad854cf4f692618f61662960551c26abe2de068efae7fbdefa9a504c78754760a9347413572789782143bda5ada31e24c6a28c69cab6a3566a693bb525c692ab1e3e1e0155d37dafa812cbdaa085e0b62a318e4efb2b569c07b7d12febca3dfe5c7173f0aa4dee406d0c70cf48ebebb56f90ffc9dcfacc1aeea1ff6ddfb39e391361897b4c21f37e537e2ed469ff1d47c5044d0ebaed0557a8bce7b6886c067a89ce103a33fbc48e2d4e8fc1f200b59767d9059bf027cd55344623ea0825f83ae2b7c18e35bfdb0cf389bc038285c6da18f5051aa75d7c0934c3ff96ab7220c4da59bd7e57d9fb106645cf1906fad3c841cca290066a7db73774234ffea1abfd9bfb659b6b7c93f5c99ca1d6a39c86b85db833cc0a2858d786e33506ec9eb35b7eff19eb81266a632fb0f0839b212b732fd94d80545c30bad461746820513c9fcbd86006e9ca08614c049fd44f37c7244478cb336a3f3585101de56c1ba814271203a6e73f064513eebc31237d08c93ca8fc994349e72fea3ea9039bc92ccaea2be8d84409d1f0c837662974043a0ff2f18cde12bec22d2f39168851e531e8a3e60d24e1dd765fb50656ebd96b2aa33c44275cc54c522fae5f44b70771096f8560cc22f24ff8140c5476a36ca1b96a98769c45ecadf1514e1684643a9d8c5f12269d09a8499c16ebca02c3fc7cf285432ca64eb8a2cc7012220aea88a04189ca74e55c1c8a53f0f10b7c1c4a105800cb0d480e00676fd471d0edfc8fb4123eecba0f9fe7b005e8554db5a9a46e44c428ae5ded6aeb35a03018c9bbf9322785f9d7ac273ba1836f2fd3bd6f603a9736c9fec162049fd5f9563e0c0572e41a2626df02a0e483843421e530e21160d3aea471d5b734ea83231abc85d1013ed45b9d516e7d48cd197676929567ca50ea8f4ad7289b1a8cee974ad33a3bc9393af36e289226c9bf1428648167fbc444b0853921bfa54b5146ce3a9fe3fcc010c25e3f603d4570b2a857577dafe57a762fe4089e0f6e4a7bd4cb8f7dda9a6b729464c8a3760edcc52a4e8acae3b1db2bf5a32604b899e84756fe1b7a3916cfc48b11207b7eb0df6150767d07581af50e40beaf77d8ee63dea1b124a1389776d2bf48ff19c26845d3409cab1a72a7e5af6c577137400ca16924159503492ea07ff30862096c92b84e7bb68c86e64ca1b320268c75bd1216f9d0b97243b63ea87f806e74dbf066d6aa606c03a99fed7d1fb3824a897d7b01842b544740b3d0312bde9b60d40619c1c725a59caad60ee70655d29bf54581874370d0aeffe071ce7ae16d8696a256dab8c5f39311b5ce5d428ace2577784e7f188b3bbbf15e746bab12d59dcec594aca41e19f3ada4f6b7bb85ef3431c7870f1615c75d20e9fb9d6309139a13984531470e136643a98b3137928fb04e60f29732c8904a1d5ffd19acc713e216de1b88c263d7c052cf1d33a80e0209dda122d2613f0ee2475ba2707570d9f0308ec60d21068201ee9ddf41ba1cfe7edc1e8c17164e19f4f50f25b8a72fc101da6558a49ac1d7205e3384d8179966ee94b25619c07716c9bc305a7d6f6842e3d42b2a27247da00afd7f2845010254da88a9395405731433cbb4546dbd214616f8c7e976871cc9edca16c294bdfe2767955395ec358c62d7826b27d92af4abdc01cb6548d877ffd535fd88b99b615a1d9386b1e182ee565eae8831479e2fe6de08a53b2e85aa5d8e40c5bf4d4620950a473b5d0691263c680c787faab3ca7d18cf954427164045fc5f00c8113465c1a924a1550d4593ccfa18aa86c20233d6a5f02922fa95090552bafd68c55f6a1dbc20b9bd023c484372e260e7d88faee40d1703382574c955310357d4d97f54d5fc83d2a664c0e2d7803bb1da02a70d1e4f721f23348d134cf3dd143582aaf0c0bc85b33db2b40998e4760a3100bf1e708639aafec710608c9c8983733064062f42d7aba67d0655306a084196b5e9e86cf6d84f61ebae8532ae0a0ff7dbc1516685d3874f9f9b9a347e76d65813bc4da253cc0405f786806d052506a8ce71ee2416e223c831beb2c4c7f1ae0b85f558252cb6c090e263c22e63aa0feea26b409031796ff6162d25fe79be60f077244cb108e974b626e9731fb9b58e9d4784972872fd584c694382519d68a81aead0e07c73bac86af3155b332c49e31706be9720e5a221ea4c9017f61571c140258ae35007f54f7b98fa132b1add7fdc4ba36698849af42b5a2a5579237e8883fde62cdb8dc5666579cd6c8b3ddcba0b0540b92692b7f4452bc2e121bdefeeb945e42bf8fac1489475c13b70dbe8b0192f273986198b3ecc7483ebeea218b138623742344d0ac767e0ea6924372441372426c6212b527a7873b12f5d9317ce8504e34b1803523f78ead6d4edda7f28dd87b4a99f7489d1ee7ed6585950cd41f80de391931eb796f9b399917c9f3bbae3633c92e73b6df434953f4ed842bb89f20442b08c3b506e8ef17549d8b1ba1fc971afcea6a8670b1615f8b302388f9791c5062c0b85812e571cb0a6287f2f2c8b9bab784645174e15166604d5811e141217ed03f997fbe7edfbfdc767c9cfb318a36080fb7218d60f024db109147810102d656b58a44f835c6c3f7b3ffe6cae1470ea8ce610ba7e4067c40e92dfd8cf7bc340bf7225e3c0b864c07ef163f47386f2b266b4579c81d41bca858ffc47039937547b0d2fc567e12fcccea178ab8b019ca66890b0f24a6748039f1b26574a8dad3f8a577c7ef82521054350e8487950232b3f06343bd90c32ae37ee7c90e73df2bd625e7c8e0454d08e7a710d3f638a7c160daf77a421a65c7f80add41bc22635bc51e2d9f4fdc92795ed3c97c11dd1dc206f35ae6f406f912f4e8221dbb2cbc92884b345e93563927c599bc5c2075ac89c6aa14f2151d16648460b33f5859ec7beac062f3432bc4d8cb6d0ff4c817aad14d742162dac4c28007d6807a30ee671b94ba158eb5ca817042ef284fbfa210a09f562912f400050b12e89ddfd0f07c63cd64174348c362e2218760a30b0d20f259d1cfe5d3bba01d91962f91f5d820386570f49a67a0e38e947c37eb910552f07fc6f7101df0e0cb7ae17abca1a32ee6cfbde807a677f511860aa2e0b7eea1c20ee23536d8670683244db7c400aa8b26a0d295e8aa5ac59138a2f0b1d68e2a9e0719a89886ac7792b7a019e514b35b140cd3f88ecb8feb1f29740ad5e6fd74fb31876506cd509cc6de2758881301c12ccba1515a5788af6302efcc554a91746bc3dbfb9d787ee2df605c18857b981dee0f08a70bc00c603de740880a0822b1c2186f6ea67e64996214d4217210d3cd90aa3fde17e2e0b04746698c7148522dc040abd075490b068c9a4eede37b7c60bd21a0d8acc86391bad54d033847c3e4fb274c133e263a9716fd64f0b9643e80e61ad3d4fdfbc8dc2265722769413f1c8c1ae42f9f11891e1c30c9ceed92d0839d0a43f4a55b46daf59cc9be155ee6b73f1b14c61b3b238f900c63c733df4451b2741b3237e264fd30714c5ac48b5c78de062473d281bdfc38aa529ad849565908a1359c9a9242f97df315c40df7bfbd6b5fee156304a2f06c0d210911dac1244b4c71cff335046363e81c18c32fa4f363d9492a592120e0b2d85300ddb96e1b8489a59330d58b5b06f5be06a88e1519a275dad9050bcb74686c4ec2db4554d5180ed91e1547ce094dc6a36208f6026d55fb4a685d1ff824ba2eeeb7638a37f181b8445902d5125dc27dff0e30d8b683c7d58b8cd1c4c6abf6b793b402193c7e11beca51042c2a55b0b410a6000bd80a0b0e4bb07ae20b87c4ae2a7b9390c91753fa75e134436e82d332d77fe4cb62c4151c334f37514c2daeb7d0920d599fcdd8503262fd288410203721469769b7a26509eef74038407a96348faae13f2243ebd1af89a9cc2f0f054f1716716f32ff602c9ef150f2590886191c67feedcfe645faf324800f0f242e03c50dc2a70c1d35b060625b4287a8f00b1ce232d1969b40c214c1454702c0fcb8f1e878b40e37861b4a915231dbc97f71d1e1f4f06f8449120702ff1f1f138b8b00e385277184a3f34d92104dbb506c195718d96087eae902f9647c3cb9a1b8a78e591ccdf6d331b2cc483f8744dab10a64c9be116716c8ea09def82e0ea3b3afb0ddf453b3defadf7bd38dad22aa4dd2d07cab90033de4fde6cf40e97015e867eb58216b74da4ec83ae4d9c6b154ac54b932bf9d409f49ab09001ef470d37d5d93817a064e57c0aaf2019374f198ec1be9b07c5f9d711a41a19a3000d405f4031b88b76aaaad2b1db3f1d173e58e7652ee502a55d7d7ed214684863e0b0732e829b3618d210afb2e92167c97957730994287a20661f513145d669ce63844917740c4907049cef9481758fa831e5494024bb5cc07cb806406c0fce4421111e340a45ce159bc7d3308f4d908f6c344ac663c55661369221ee5b21f1c763fb982accf76ffce6cd0bbd0ed9b2fbd006b5a8860de635ebd2f62af08bae0d97dc3a942ed94b3949646e6696dba744de93a3bbae9d7997c7ffe5f9c8a149ecacae185a5929e3e6a61794c264f4c7076fa5e19fd770abcc2b80b34be6a1a9f00df701788ac9fbc14f61f32677413581c0c849d9c45e2710a9de790ced558c6280df839b20dc43d749501d6329237aa7313d9b2934b7d293741d585e41ce4b018d5ef2117cd9e789382f62db073db7cae700629b7140a080e22b88043542fb93e16baba2a841e0aa7b12aa8e8832d53456430d507e7e31d41311ccdeaf9e472019726622101dcc91baccfc2eba65d537631c58f1cd8679c374f89e4b915dc32c75a8361808c7092ae738dad215b8c59ec7e0f3e4519981300bfb45b3093dc42b2273ce2c67ff458e0a81973349608e305081f6f020e6a9041bb74080a9faada4f62e2e8facdf48d28d175e89bf4db36ccdf04bebf52729627a762388160a290b11fea2e5645d840c4d25dbe111da9673ef441ec42d7e6fa35cc3108defa7a598412c4ddf2b9c1b8e08641dbfe95ce27073469e888e563ae28c2d83980b6d6e06add5383414e6eeabcfd37a7a0e4e00baa42762c4f81b85976d3e4ea463291cb2e340105d205e01238e47b3bb391983c8d89072e3dd4f160c0cbfd6137196d2b088b3f0a048f67a01f1c760f1a1718d964eead68bdc8a77a57649fd7dae12198775a54066c3b01e68eaa087b011a69ae02b271834b8615fbe060bc52294540108059f3c88c1321ea4a0a0a929ccb5b9e879deb15c417d92c42c51eb89833c2ed845fad4ca62c93b7a6f031c96f9a5dc86e5197269b0309f13daa16bd248df66d595213697184300dd5c9396221af7204e4748c04ac568c66a3a2cfca0993a5136b50badba0781b65a86f994390e37b09a9e82e350ef22024167c8002085e019f64b2ab81054b285ae1c7825e2bc7e5c3bcbe115c97d8a21cdd370707bc956350ec561ab78b2d94979b37b86cdb035101f649e6e30faca123f19cca4debef0dff5c0deba5df3383d2e178b0f679fdfd893f14fa86db154eb268ff278925deec1432d9f2e6da58002ec6f7c3f6910fe479a369812d39842d98daf0893f4f5a233ae8274fc274e105d229217379cecd7ee89c275872305e920fc6ea690814d52d2dfe52f5110294e3a99430136f2395e4d2da07b8504ef4655cfbcb448172f60773b533175dfb0704d5e182dcf39a509e441c90d44113a4bdde0e0463034c3596051c079d5939df734c0d2121a28d598826d15f7d8be7ea226a204c66f292ba445e9a6a29a0fb4f9f1acd8912b809be6ca61696ff49ea5a105793c29a0f84216ce8010d5bb29c8fb7004dcc37bbedf2f965dcceb731eaa8aecbe94174273166027774acd194c350187dd391b480418157e55210ba3e2b5d0797435d1596d844f2c4166122d3ef415ee963f2442cafc163f4e134e72c2ec8b1aab254075fd9b02326d31f0493cceb41dcc94662198f18c98715d49f5d961130ea58a071dafc81c1b0595c4be888e7e5c960702b917d9ab5c74fcbc9b305f6cc812a633fb5ffccf1b4157338bdea6048f0eb9e8e6517855057780074bb7b682189290b7b6121d5221656d5d36a13d5f80accc159d116288212454dd9cbd5309bfb5b21c167106dfb0988a901205742a7e880eebe25d82eefdeeb9d360a8c6057dd9291e3aa703fe89e2cca447678e024de31ec7a9759f2cd75f11682a2a83cee63b5b40a19bab5e4a419a9c91698281061b415b40d3aec84d96d104ff5c93e8b78a93e083b4d3aaaefc94488121df2b354632f965ab37d520ddd689508b11cf2bf1b17830b4faf41944f467626baefb8d0a2a33311d9d813a3d6b0cd8084dd218b9cee12c81b07ad8445a8d7c8a751a379edcf75cb97b61bab80c2a493c3a99bf926a85fdac76667c21796b063a36432a480642528fdb009214df3f961b9e12b74c6131417bbed4b4c0fe3c4e4432af309cd77f3753e453320b6b83a125ccf9b534bb78e336f37f08eba9dedfa26782b55cf5ccae334c435533b3b3a8ce33bd21157e50012bf0beeb0de2008a3d525cd71ebf72f1ccad7dfb131fe07948f481c90a772e2c7a4bcad5ffc548691a898c0d6568a1c2859b795392bc41d15cc621d43f9db8909ea1103e7014c1961f52591fc5334da30430ac959c30283edc57b537c459f62efe0b6558a71ce233449c5c9bfda9b715c4b9a70b215aff75246c733581b8425ca4b126298f92b7b805ab77928e6f01077fc893400fd7e616dc15732e28da38ae882b2683328516d5a317ce19619ff3284f384335bf13592feac01d9ae70521de20b9bf88591cc0e18bfa8343ef4b8f03b40d1ae09512e04088a30220a924f3116a8503343110d1fe78e805b0e415185ddf5d23cebb4cf790d25acd67101fc7cae2ec1497e60a92ce4a64142110a7e3ef3440399ad110398b57d3b46612d0ce72ccac46d34c54a06e907de5d1409a44e02a20828e91d991f7d280f1609111d1a04e76a0f38f59c88936bd555c234d01b1cb5f5ae27253fac2919d4c65b59925c2808b969a6f7b144a9bcea160082611056df850fb7e19a8293066df8218e8ec457509633994f5ef5d7e1705ba49e87472f41e83483396403ab29b3e81fc4579ab2a9f4d7e60ba3035b3fdbf83fbdbba0be24ec3e35da24e71a1f309e93de2111a570f14536cc5f79ddae1c9c478d34356530555ce3aa80b422f955300a56456c85de2c2710fd8ab1fdd801101ed849f8eb25b2ccb4aa813a07b211d0d257cbd0481279ac7ca4e77afbaa4a661abcf133711eb771fe1225c75d0af5d6b9fbbeadf6cf7dc88a189581a56c49bef2615fc0f3d0c0f05f95826f4ff6a019b865d8d18b8595e4b5cf21a0c2e687366805ae4fd2fa2523c22712bfbab4675b162f507da2d6280e0ea63f13ea02b4585def32b4e213a1487b6eb99fb5d15c2eda357634f65efd5e37fc09a10980168422eb87eba629e286d1f07e60a301590007dbd4bcbe2a2e90e247a92aaac608654b0a181e9d88796d7a74a5eaac8d94781950bfea4e0a04c803ece76adc32cf92810d8a4f3fabf1fe370f62ca1cd2211edde01788ee841150bfc5bcda3d65b240ec39c5d11c891fb14f724f60b542def1a6260d9a5c45596bc60fc9e7d9d1662c417ff944dcf8e9a4d4bcdd6632e4a56dcd364596ac98a30ccb2ba845cdc22c556a414b9d1f2735b8d3917ac4f62472c627fdf8b145cdb5b1c1f7c55560b46d0daa586544aaa35046c8e1abb899eb54f007760a4f63d218fd44df6238c1deacd134c51475c61f6336a773488fd7af452c4418004d92cc96c89a8624858f9c26f2d5f4c63178aca23e530e0e70f2524e066972486915ecc717b2a0f5b0a08daee20342e06f5e8a64a30c6f3f2b23616b89b5ef52e04b3a0c85625c9e153b19cf9b43183be5a78228eba2e887eff65736bb0b57347e8c1d4b20fbdc34460d425b3c69e178a8bcb9db8b95ac7e7ecc3e2215b3ef14962cd5f9723323d30d8e668f632042baf1953232d5333c97edda743d22a0c54ce3d5187261f6748871760cb7eccbcb2f50484a0ea25e77dbf49e26aba51b222fdcb0d309f137d1aa8fde21b4a61786c61cf8cc466ec2cb4c73a14b549f1d87dba97feacd0dc26a205f549286f59a87af91047a0e6f024e3aaa972c7f64244112d8240e5c29c93c6d16049b07f9a7b58140f8163e510b82a482ca1c2def49a0b6757eab15a8da1f178af6ce51661e9fd6138b3f095b0ceff4282519f0d2ee70a17fbb74a7612f9d9a2a78cd2b92b5e3256dfb8e8c4bf2c75729c1b1c9da79845d3542c3f7757369cb9fe958c9da5a500a1380fc0b9ae8daca5e2299261665bff64e6453b46f98bd34dbd5e7acff31f28c25e4152b3a4a51bd8fd832c9b68b1e3fb74d8f28cfa40283c63f2c67138fdacef89f4ed526d9fdb84ca8260dca8827d06c3c46a2020d84efb6702e70c9e18bcdc75a39cab020ce99dab5fb71f02a68e499226e48a9d9bb4b7eb845cafb68c095fb698dea9c2a7b1f44a4a2fc6c59d9d8b0dd018137c5fd9f417066ddca751826382ae67eee955bbdd1a42de68399d7089ba1796fd6aa1bde8bcb245d100ec5ae295c01071853f6a3caaa8ffcb1b031f4522efa9d2285830207751c8f5462f256cff6148ad58528128258e915d22dbd796698d93058a6170f49fc890177b5104ba47c567010fdaaa0edd0bba9c59278c0386a79518b2b554ba569f72858b4145ac27ba59a3c38b2b400a5b751ee440d6238e813703e7bac9f52c8e3699edaf5b1f63b7465a11b1a80147e5178c30dd9fcae102f78ece3472e92435f98b4045e52e9504613fe7a724f64bee1cc4d5e6e3c152428500fea52536b7c5c14fffaec4e9771adfe5345940f9920c8172b6c428304a801214564a56bbc32c42c83325cd4433d6f0181809b99dd3ff33b16e76535a25062148ad39b76554a7431aafe5704bbb88334db993de0aca0b3cdffc15c461ca2ad3bc1d808ee640b4c8f07d581fda8cf581c198cd6a53e01e8b8a96e99556eb3ca1d8b9d9158b37e91e3e27360372239aefb863661bd3bda8e01b54802f530fbad3778748c4580c8c3e701666ed20197751dd5ec68552c494a6074fdc2577c74d90d3fbf0393cce480357a2ff4b25e33a593859a080cb08cc753e5e7a89078ea59deafcb0bc136cb65d589d607b434b489d7a3edda0a0c675f0fecfbef47c74ecc3ae13643f9ce8a65368b7dce512b757753bfaca389e0ee45fe8e44842d033ff8f2499a708e443bea821971131c8bff3773f4199d07991ddc9f4054a060ea77ba7f6ee9788de2508b2b664fa62e876527460474c128b19f31dec9974a538b79f28501ed8cee27459097c8036b1f3626b002318fdfb77e74bb170e9cb7b0b9cfa4e474f7aa26fffa03323a9e27a79080e613f21217ecf66945a2447ddc651427386924447bdf51461c42822bd79a4508c509676d1d602f30ff9a8cb2eddd8cf7b4530dc88f291e0d6ea692e4d60b04c0f3c012b31a10ed13dc0d6dc403e57acddcc72d47f870afd5577a3bcaf2e3d51b824afaee044cf0972b9499debe57d9599df4850ac389021b51b1f72090593951cde0d60da87a04f3daa8c41883a8e16b42cc3bbfdcb2ead725db53c8a3c8dcfef34f88851e266d70f63ab443cd0926f67583606b4d5e0b2f918c95a7ea90986b320bbddc78862d975394fb53cc168468c3f3bbc62d9d142d9ee9357f442d195daadc5df748e38278e640390230931a2fc3f92649e229021dc278d00a03e73b24b84693e566904bc106456576f55e0911f30eb0fef720f46c5a4282a1f25c4a00bb6359b1a5dd7994d5e1bede35b2f33bfabc1f19be10f5654ef496e25fe4b6b5a28f58295f1a0cc64c86965e8867697124f4151162a5b3155f17330a8e889800d466b20d402febcf501f620044f23eb012e865fec761882876831d38b062c2651d60d36d140e4a43a3e0ab1c2343428ea7e2503a844e2bfb88968a02bab80212ec2ba46afa2a986d000430bcd2462d47131491776ac9ee1c2c1686289d9c9758c4cd23e5c517bbdc79397142c13e486b16363a1d0d0c09fbd4d6b6a2eee91c939c0e080b51b5068f84639c8f8eae7db4adb7260c11a3ac11ad6faaf9554606bb39cfd3a8de9612d9946bacf57a1efac87ff5386fd83dffe704dced819c0df0ece3b823359b470d675ea9f83a2544f9391b41718527ae7d88b5515993a986ebc38564f78a13e054216dd2370e9eca9b276e205b0d71528e7366e58d5f1db07ef2b10684b1dcf0517140c5b987a4bcf43a9184252687d46b1e1bf4f42245aa4f38663bc27e20159d88e38454e6b5dd513bb45daf6af4c7f547d79ed3963cd1418facffcec904c7c37c0994ffd090e9e2c34caa1b12f564e5b21e2c986688e87575d3db684ea64a175014c8b9b6a548ee55617a00ecae8e924dc662bbd6a1149062b2a23824f50a1f83199fd2e12622d89eddf0a55e6059d84e1ac695dd1c11f0ee8466576df60994d99d90a20cd3a5f7c53d0103d8b9ba1aaf35fff3a754558797b1ade12fe8ea6fda6eeef08c798663f8fbd3f7c853dba891f497586b90ff4dc0fe5bb0d7fcc2090f06cfe2aa8cb2edcac97356bf2d16aff549a51dc3741507f9a81fa48fa3bb07e35a0441f6469eb9e37f35734ff1455de63b2ef6bf5d1ba4e22a19652a214936548c5fb8d087e4b3cfc700036a24949af38e079a5099967ea36d18d99fa9b4b53e0de0dc4fc414213ad183f60476cf28708b72dd3d9418fd246b2e7badfa34fa824ec5b5fc892755ab1d7877971dc21f493444bd896fdb75cd89d4d9a8c1bf8895192381d4b8b067db13db4cd8a6e8fc50bf9860016f69c850f12e5a52907b2cf99558a27d0243d4ad26ff1738835cb2b816c6a68e0ce4d1d1e494053571bb9a489b791331c499ce67cead7065bcaf34423998468bb644313958864bb0dc158923d1cbadb1b38ba16e9976af958aab349e9992857e80983d0e40cc64b414e28e4cfcadce1b4892066dfa02a644ce6b9e6c5cd88437f064d4a7f365815f7bcb0ae4959bfc0ac544d861c711d8186f6e431b1e1f31d8d0cbd5335bd303bfd3d258e106a7312a7742885c45f99ded542a52e10f63050e61a09a6af197a0f8c30c475348af7a273e425ea5fad46817cf07ed31994e734fbc34c2626e8f6720acac2ae7c7bd7ba7958d334c308ea236e6f12e2a1d8e9f95b3583a17d28196fa68f16e863d6eab308082e580194c09f15d70c8fb39369585f656254a301cd0f02a86b4732f1e4dd1c87745772bdc79e66545fe9ebdd61c6f460f9ff71a0006c056763db73cef1615ac926a9c60b5b4d2ede1811c9435c6b63548f861090113103135ba9f5cb70e0f6eefce39b78ce17f8f509f43c3478a2124ac54ccdc1fad5d0538ebb8d37de5a8f9cc9c9434b703afe2e9e08bf32624661c15d5afa8629940923bedd4131dfb1c1f891c45e2f03e3452eeb4eaa508d1fcb93105a7515fb2d8588079c48626e90f17bdef7266942d16ba1c9744ac0f8cc82268e4094d26221e96c8492073e8b34fae070df0ffde181d6ff1e61fdcf11d4fff48040057f7e5d36787187b0cec056bb96913c5caf042c258d9d41b3cb9da7b5f91a66557ae4eb6010af1ee191ff25a95aa3cb7643f0b41b23f322fb9009def4e8344a4cdaff2feea7458521d1b76d93b1950872f22af7bc24d5c3be884bc06c58d9c58ad980d579e10ae381b45fb6dc662e607541997d002dc55ed1621a2da280b392046181ef1eae02a9441dc1a9bceda0414793c0a8ddc4d60c3692b2b37987b5c13e477a271ebea998e86f0328d817b7cbc909c8c633e06c83f0af9b46dcf987d6ba951538e79ba8347723c2a23d487c78aae4bcb558685d4036d9a2cd04e48049ba21d11cc52db584df0f1732517626f681d65151d6f1d4405b344a1e90a690d496fe8772f38097b3555a0b7aa890b1b3599e52a68e89142ac7332f851cd6b48964cdc6aed29887967d6f92bfa24f813fac368143d27e3a174f28e3221fe926826333169610f634b8a7c1292f6e39219899626bc9908fa965758ed68370b6bc01be08db4cd60ab8b0c5fc7ce5fed75701885d435d78e1e24bec97de96578909788ab8c18cbd30cb872a0452e93b1f72c57c735fd3802d611bb1df07127199af1f14ec80dd006f1b5c862208ba1ae17fa4707aa7eff1c71bc6ea4784cd6545d937d6104d6dd593751118333cfdcdcd6502bc4e909701f9893c2f623741c601baf87a002f05b54a3b217cd100fd90566c0280f000e951b5f45a2894fa5cd5b247fa827a9d97d95cb1ae09948182e08efc0607f4bb59c723c54abb7777b3fb318114e92044ea50339caf6dc0f6caddfb17696df6515576dcf58f7584d962b316ae129596651d000c3c186e6bd7c7fac29eb2a6c2911259c86bae6795c905a1b5e829c7cee22d4e6f565e1178efa74dd281cb87429a6584ece4675c16f3563758c53f91b7fdbe374eb3741ccae4649819a86abe92003ead60415768cb590be96344659a9bc39297862b49552acabdf9f795a1193f6abee9cd9d7ed180c3e387b167e916608e4b7ec0c68ccb8ace92c4987ebd35cf394ea1c6210383bc0e60fb1b4ec8989c80e6329d1dfcf454175c3240223eaaf6394e37f084769a77b80aa9d981dd7a553d583c18fbec87ed673e2cd9cad76b1fac6acded3bfccedabea106f6dd23fda8b22a230fe5fd818c7f565ba3b92c7b58cd563bd0776a7c09d356f4293145b36b137a86ae56c378d3bcf8d9dc8f3eac4cdd346675d3644f47538d3a76d29a30570f75c546bdc7b60982acd55df2a4333d3a6c50e25235d82aa1c2397ac943ffe7a03c17f6e44bb01e9a3771455bb3aa75e011917f278ad125b4a481ce92b8e7dc942d9aa66aff8e7a311bfd30736df734714ee61705cc24aceb1065225818230fdf0c88bf98a3425ca3e6cb0c3855d522ad685a67c26678121b749d274e930e887087579f8a9f30c7d3a14729907e3df4d2531267ace16e4ae1149db718090df9e8e4120c51039e16f939100cf6dd57780118a8a359d2c26bd5c17fb4d3171a79e7b081397444bee0b53a98338a5a75b119c1e2f621d86e3c3afc9f8b6da3b2e77730e7d054c4548d7559f0198bcf4507dc2c2061043dcabfc32d8e6a7e0991fbac76638697f5fe7a64dfbb42e10c3ea07215b259d0f1ce48befdc1a6cfb4272ca439bbbeaebc6e528ab7a0afa6a7158997adde72cd799334e8d053ffe83c5520332e81e0a5f3c18bde42d769a66bdb2097be092257e08c62b450c72b4506e3aef1d176a3125c1df14ee398c446dc6024e455fcae9b9cef7ed7b450c874bc83a7cf6735c20c8058282dae241bb3970251b6f5e068f4bb148b5b6b2640809eb339c94879ce71b500ca9ff318e65d292f0e78545dfc3966665fe094d474ca6d65d038f2e50cbd390ee5ea0262c005c02daab861090118497cb420f1794e3cd2fa3f441cc7b97b5866e741ba3e72f1e062b76b1162f937c0eadd224e1d647c67149f7a9be53395c8f1263c4ed8b377bbf8becbe5fb0c474b441b8efc38f12a52de2727db4ac548ebca7a6be91595a0dec1a18ace5e2a6265eb45d9bc7244a4debca8a5d34332c2a30e8e30828d48c01244c02e53db2c23b8364334e9aca779a145280d8bef87862d8e93fb82a1dbbe63b75246694f1ddfe82620ad85f31ee427434383a8bfb6fa973a0dd2be5017ac4335c18f294b41e24aaa351026109987dc7f3deb54aac43774f650d13d1a0685d402dc941689c293c1226ab1483aa187adc686b307c72cdfd10c297d1516486a9f03591f0b13d096a09c1b04f8b1fcb295faf7e7d1070f56cf9a90018a22d1e99ffc8a17254030cbfc6f1203efe3a526e5605b43230bc3d08bde93f998cbb9a8d252c516126fb1015cdc7ee622e0646163783dd5f091a45b3d19d227f83e5824f33cdcbd94ee5e915f337ffc94fba486a9ab29821139f888e9e87f41c528310f020f0e75376d0011ae94bdec8d8270081a5a37de2845899edf948b991cc01b57260e6259e78aaea0806c2d26fd18ac276dc4f14fbca6a7519ffec2a25533910e81f26f7c69f384ee1cbaec47e74409eb689ef5b2401971161fc6d16904e94ef3791258ab3a1c39d2d44dff0586f44174df6f91ca3ef814d89dc690c76da3f9f497bf1751ed3ef870e6ff5212f250ca7dbdbb1037e41c434ec22249d7be9ee1c199d9df4f4c732b25c5fd1b890ccfed1df08ec91d55c173ab18a7568e96089027d4f22ecbeb29cbb5415cec50611f4aa4273c06c974d5897452e3caf229ba48fae061c4bd551f1de1a01e490fe7166c84556a337640e0f7971ce812a7e91a451dba86c0be75b93afc47d200b029e0604b43e2ca7f01028a186ac08406b14482ba63b3de7fe49f774005f1a4a811395411021a7952da840d2d4c361648aafcaeb914e2fb4ebbf06e32dec55052e28ada8a1b9843df3e0af7794367db96126cfc6fddb260678ebb74ff0a039491360cb2eafdac08f809288a954158f269ddbf9e1ba648cece912b4772424c680cd9d4a1fb61c8033688af3a5f158c3e41d1c4504f7889d40aed3801aa4307e8aa7ece851a4cf2721df66f1908e673242202e207545a257caf4a2d59b1162ae9bd4e0ce3f1e0eacf334cc9e91887f9c7d374997a9ce33894aa9fe81d7a15c1c4bc1416a6f5472a9cedff41555f4fed8117dd0f0cc38c56e5b60cd47aba2c52bea552e9b1c5c015a3a3c768c9efaa392f63f579828d78c53e650561ce746693852a669c77805f96f1a815a5609c21813727c8cfc55954b82cc814de2043f22b8f8cd7c492fa8472c3638b86340b497633f91a79527349d712224f1e8882b83e6cb62ffab9782f16ad29407ccee1f62b46afdadfe2d8fb2cade6ff3e071c32c0a508854d213898f726128201b6906164788b43f6010e7ed9a31af32cecdbe002a6b5a48e6a887d03406433545da98e1ede07e262ebd03d9dc29138204e83a6109f5456400ef231c8080b9ebda15842188fee9fc7d5c5a0f77ab037937002f39a499a07dbc7bd27dc5a6f9c2bb2345322abe6aad15f617115bea3d6a127535932bdf31c4ec0df021db9303ca20a9043f4f715c719d811ba0c295b2a288caef9e915ccad6d2529f6936e8df7a47aad5feaeb1528e67963ae44546c92b9af22fee42cec598b58a06b63f008f497035ad906925b3090ba4a07ae71b8958586bf32dd33856b2071a2677a6d9e89deb9ca266f7a2044a667eac03f5d77b411527e8331172b5000040ca4e2648f3abd1ea4802294a8d643d59a17a60195bd72015d1a99469696ecbdf7965b4a29534a010309060927092ef94eacfca6e23cf25b0a6b867c62fb59b1a7451ffbd8d3a25fab04fffa37e2639f0fece3d7cf079421ece3fba8ef1be6460eca076a31605efec5afa544978cef48d1eb14fa2cad0782c736b0e70f27b72b545d6e0484fc74e98737979fbbc7a75df443ea61b73d6f627944fa426cc37b22470e43b9fbd5bb0bdb77ef02cdaf9ebb9650f7b5e6ea6bcd6f0ed110c5a7f98a5efc85fd892eeae3207d8983f50165be8fed57926b0fbbdc01ade4af7e7e43ddd7c2e40f6d5f0bfb6da8fbf8ddd7751df73535dd01117517012120ddd3fc169d3baa68ba4a43f337baee69baa84b9f5f9cb7eaa088687ef3ae25d471277ffb38d66f687b9a8f28fe76b31fd637dab5329ac73cdb2a41b57d45af21fbdd0f6ddf7d4450ea13c9fcf6f51ba2f9eebb8f68e669be887612f0db460ec3e50e88c841daca3ea1ed4de0d7f6f980a2bd0ff9dddf883a78d17c3e7c28fe6a45f33e6adee6f3b13dcdf30bf33418eeb679ad1426d0b78d1ca40fd412aaa9792120ab8fa685bdcdf3abc66b615fab84ed577f83e66d3e1f354ff3abcf0794a19a8fe6c3115d74fbf835e3054517a5d9ab38aafb21f9ddc7d155bf909b86e2739f4f74d1c73cec663f6cffec5a25c8df7e48fef6b5eaf38ba36b287e8b521f76519ffa8516130222e95634bd8b62db90baab1636747777e92e42e7eeee518a4dc741fe38447a163bb3b03ac260fb59c61861a0f46db0b24a0e36a7f1391de791df23145dfe93bfdc1d8d007f1bb33b39812a55e8e120d18e76000a8c007f2140a040c05ee2c763720f0f223f35350582fa35541fb92d43ff1003fc58dfbc0dc5351a4ada60e3639c8d3dd9b3b2674d27db33466c5e7e1936548c53dc14fa863c1567b0fd13a8d279ea8cde92897a36850e48a3bb8c89c26a979d5ae8c2a5ecbcecd4c214d716725862dc90e55a70e5a6130b53774e76c15274cdaf97f31ef9920275e66527168eb8a14ea4226b639c90f3c8f8cec4624d51169be23b589bcb4e2c2cb9a14e4b61e5652716946e7fcf8b85a4e8aa5e0e2bf151c4a0e2cacb4e4648dc90e56258da1fe7612b569eb8f243081a61c879fce5e3c8c136f84a8f6bcfc9c80747a288c9083161949808890105525ea85801184e30397981498c4cf6f6b368d855cefc3732326a41ba3e082209264778004304160fe0a08b1f515c38b185182ff95c7b6039d8dd10d819f342571903c38d638c51c718e3b6fc01931f62188661a95f0d198adb51b3bbcc15701fced7beceb9cd39e717621b36359f4c1c6d3295254b965f88bdf61ba6fdc5be4fc0e46b757aa1f6f33799d52bbf4f905974695e8f79b14ccbb2acb2dcb09f1e3f36e5c43cbe72ce392586c9a75e3bf990740d70d989c90e6e77744a11f8ca9f7262897632d1430d701882a379f6c4ba8b838cd1d954cc8ec2d154ccd9a5bb4ccfa70d50392b16131be6ac7c40f8096195b3627153d8f02f2f71fd398b021bfe3f54d316a56cdbfb331824ddcfcd5e6ceae67eeefcd93b20eef7e7cb5cafcf598402763d972db7909e8d4b365c79cf0e7780373e67651536645d96f338c89ec4ba7b95ad65101b4aae11830dad3f2bb20d8e5f33384d70e96f1d3f0504ccefc1ba92e84609380a718c981b3f7624f4981bbf1428d798fe22f04b7f8b28cc9f48367fcef2c417522fa2ec883047af15d8294b0f2f106152bc56583979c58f9d0121771bcb0aae3cbdb8092dbe687a3154962d8e5e6da4065d9a5eeeed44796d745219bd79d24c15165de6f28392302ff7726278ade0dd4e7c76ecd83471c2c9cecab9b813510771061efb890e23919c62e8305c729ca787bcf19d9ba40c66cf26582af3c224881d2cc5a547eca8d3c37bf86a013668c162c5b1134d9c70c2c90094a1e58a2860d401ab004e347982c5753ebae7645e95558deb643a99eeb91f927955f7dcdfe838199510b6e3ba0fcbaaab6655db36906bd0d81941c8c78a25383c712db155456ca41dd08cb31b81b59910b0343978e357bf8e5270566972a5a011b6c4c6a1794862d4e508c979b80bb7657577f7531013b54289522f3bbccc6844ca7fb672a691f970c402d20f8f98986e72268fe25496967a69aa07dbf0a5d8d3c35dfe0ca647bc218399b3472fbd23b9924fd90777a2abbad1f53061478159f5d93c107ca696c5da52b025d94bd45d890eb1a1bca12b5d3f5a3a5a02d2820de353ed683d1cd1459b58b60100b6d1310a7c7a9a99c972b0a133d18e3189db01ec7129ba9cc979b428ce9349a132157b6c3492095dcacad6c1e2b12f3ec5a7b8f42dfd92336d5d0794c598c5ce9eeb80b2186f165bcb94acfd5ad9f7cb7ed847420da523f3724825a72db79f8ebe19999854ce92a1ff79e2773fbbdcdf8a130e6e7f4f9bb9310ca4f1e54a7b2069c5a4a415a3dd951d310c049f70a6ce94d8ed312f8bc1867d84f421d801981e62acdff0fb56a425fa3b3f41423872e878b01e1df9d14e8c744e3aa7d43cd05d5f1d1b832968842d2c0f6c8ed2121c9ee410e79c4b7078329ba3ae5292244aced3b21bc39ac653105316a518777ce18569506ced316b64d446f3ff1f7b414f753f4ddda6a6d94580c328c73662cfa0b93497b75f43c1719377bd6373e9e772eb6727b05e5581a53bd88e8aaee6d223de6e621a5c821c8cdf8f109bfd20b1ddf433c5fd601a450a1042b4d2121c92d81c3c48d24c4f9da4999aa99970cca8801824148589294a50bd812bb78a3172ec79a15f50628fbe13e8dbadf849c06f7f73ba7cd25db9959495c6ce94c2a79ea09e5a4b6f8182827a828a3d435ba0b6400d0dfded282b256596c4daf4d113dba0cf6ab2528e58efef46ced39279bef36dbe56cdf76bf538bf145d4fadc579b4f72dce53ffe6ed17f6942a7e4bf5339e5baaaf5542eb73fe868c9ff1f998f1323ee7f3016568c6cbc8791f32bef56a3dbf30558ed79ac27acff01a2abafc65782da58d1dd0f6436cc371788f96e6d24f2f309058d993a537e42dadfefe8470c4af47bc33607b3e20d0f413bdf16c3c69f3369f8ee8f2bf390aabab64ec4f507439543fb1cb9fe6098afb1c2a3a54ce652b56a0dceea7b09f745829992fb2541f083f59118daf9ea8336929a3821a816bb83b77224c4e462523a515632e20c300ace1ba69b14adf34aeeb40ae41636704a1211e9c674bed3d5a7ccc03f24366cc9831635ed803f91180a9fa2f80afecb7c63c2050e40379e15f3f7acc0bcafc17c01714faeda5d023e7a99742b7e865e30624969a8c5ea14bdd5abf1326527439d5bf6dc0c1cec135dcab424f54f4daaa47bd9613b5fc6b9540bffe10fdfafef52382e2df50fdac7e44d155f42a7ab124152bbf19ae11e7cff7afd1c5352235c15ffd145d1d8b588e728f2cd858677396bda30a6143b0fb40ee657ee3b0baccf7eb709e78fb39e78e86b773bb0fac01d8745e670ff989bb2696efcdeda182f36c1f3ba0ee1d0ef6eba03ad8d50d592aacf5ea701e16813f04016ceeb7c89d82dcc1d5eafd38d87d77bf755e0acdaed2e160d72836072f10c2c2ed246e28e718ab63070fe7d19ec5dceebc10bcddcb78217865bed6d05e8efb4299d77ec6fbe8eabebb08c8bcea7fa2f5b697f1f8aabc1de0ddb8af973b8de3b67a33ef07e407390ebbc27e50ecd1c1ae7e97f22fb497a5baa986064c580ac50b4f2861ab122c3cc560331750b1744a92ad402c61ab0f39d84c0988a537b86029921f4a332c5172c1b9586c0c232c8d97ce01154ed0c0bd580deac993174e39340dc9614a0e8b4e9f1c9c0cc9c63c6d39e420a44e69392011c19ab21c5c90618c29255f4eb0832725369841c9c818484a94383246c9141aaed191122743c668d2799bc50a3bb3454761ab15bec55a27dcc9ca64e9292c4744831983c90d5675b9ca18364cb13797ab8cf143063be0e29b54197384836d5b790d4d9c71eb32b6def77655c76dae655e317f763028baf8733a65f489eeeecedc43a7bbbbd7e8e29f1e47d7bb945fc8baec6eb3aaa199b132aa8edb341b5501c0a88d458e279310807bc29cfc5eb9877e7fd6de28734f886074f1536f36d790711ec1507388749f9e478031664aa44739354ddb32a5a4a4ca715ded23a336ca3a15765b0cf718eed11a2af6685f47390c1d15c612bd0a0e56b12a954a866a1eb32be371b34c93e1643aa0fa32d646fce8f16d45a06f7cbf1d99769d4c9ab4d6cec828261d5139346b3ba0f916a389421f5410a2ae188009bb3b2e76406b01d906f64c4f4241934669898ad230a627fa859e64e7173a12d3130d0d4d8d77cf4d9df71c13d615ebf94497bfb6b313eedc1aee6b561c6fbf333fc47a3a2a4e76403335029870745db1ed40dcc8413f7297b7037a078c007ffa8de11a9eb598195acc2ad332af2e638b9c52d28a659926a5cc5e4a2931a94929e74b29a59cef8160d3fc2a65629bc75fca2b6494ab1bb3a74dba469a05b10d961f6318e97542f23db0ded824bfca43b7c7a75aa5b59ec4504646462ea04009cded244b6a0d1e49b8d86ec8601a09d6bdd474bb9fb496ac9d9428ddeed6bedf817ce842906e37f113873162e29ec2e1e96e2fb7d73aa04dfbac03d2b2306ef6b503caaac4923cb9d8d30e08abb2e786dc03eefc8a822dee7c46019355c0951f2597270470431a1f80206352113614b1218b1b5c70031527d0e246302fb74977eb2c8c6ceae752ab0d5ef05df9d58a1fb05a85d3095ca07589e66a1872e5cb1aa874b161ed9eb8cad1a1117e0082945ba0e872a5978ec55c0900d948642fa981ec285736152426ae7cf9d5b21edc79e2caf7f909123252c5958f23c7912b5f070f1cae7c158a34313ab27404c5954e46a6b8f23b49e90896251bae744202e6b2130d46c86e72b281b54e4844aebf5b6baf962c41baaeb672b6720fbfa21d00912caae58cf8f0e6e763febc1856b11abf620e15976c0804888e1f5cbe504adf3d2cab9a46e9f3a094d24a29a594524a29a5af7999178451faf4896d8a7329bdb9f443cbd1481f876dcca77fc3369e36d689fc36f678fcdadcabfcb2f3b3e757d69a5fab04ecb5bfb17df6f9f0df5efb7c4019f2dfde47dcbc2a334ddbbebb12fcb57d6b484999d4d6463ad8066347524918b6a3793e41eee1c86a14f6b1a2ab071bb2948e1d15084f01d1115df25f4e8bdc67169b058a5ef2fb37dac476a05f07db50dd7ec6c16d06e3f6c72cdcfe2d9b625f47ece9573f8fd8937d6bdff5fb5b29f660dfdd49408ada05969fbb8ee2607ff683b57d4a8de476536472c2525c9395a32c4b30ef27e7692b47496e3f942737e49a157ba46cd95dc9357260f94379bb2544e9b76e4430afec676444588ecf4d4324b9ad187b988a291e447dd4489e6e27f1a931477783aa12c612376c23225c6ed8426e7752746e27b77f9cbb6672b0abdb67a44274b58ee8eaaf5e1f755274f5536f56805ff2e3ffae45d16b101beb705fe93ced34de41f9ee6df6ce7fb584e8471dbc5a435107affa4ce2e3601b5d8d687c1e3872c41ee730ac23f9d125ffc7a3b7e31344591f2bbae4531f6cc85c348023be3753121856e6b2d3901bbca0565b3939848b919d212f20c28494e8e23aa4290626a4a28babce77995e4c4a63c81097d2d6a111ea8034ae908aaea902cb5d761ae2430eabcb141b33f3fc003c8fd9aaf334f387925976436554d81c9da801e7e1db96bf2c7173638c31c75dfdeddfc462c39cdb4fc37942906f8c31c678e307c5c8f58ec4c2ef37624f310cab5ec43862118b183fe5c9fcf393e10ef0f2c79f5838c6182312b5da0e280e395203ed0255c598544c615524c92a9ee84e850286b8e3b3f3814f17a35967586206afb672968aa4283b205a63cf4eac5684ecf87c3e36d062832d3bb1a762334889828ae2e22ebe61b5b7da199874feb914e880346260b9cb55c2b03265732e570983c90dc2407273b98a1825f8e7d7287f7e9557b294cfd39b89ad3a77bf734b97e98ec2f6ffb84befa8720753e56d199ee276f7bf4e3f8d9de8e3e476339406fabb7f863977eadc49e3ce2dee7c39bfce2045dc29030c77cac0c49d429400cd9d8a13c3d38d9579febaecd93208a82bad8ce27c953561434e07f49e566a26a040e02b3c07b80c75e3cbf8f5851e6e7ccb7aa1e9c67f188adcf8200c5d6efc9da3216e7c1f176efc1f21d729861b6efc0a05812eb8eee15702ae747221077b84033142d0608625320802478c12449aece0a4c5e9d5c40907d8d401b2e4facb18af60b951bb517e8c3122e04717d7df11e04410524048c18230c2102161240a074f44e9827db0f2826e63bf3a467e2501d7bf572b2bb65061448a4b134c8238220811426411c385d7108e1c6ca32f2bf6d28d5890b8f14368ceae84b0a10edffea17475ae773e205df91b8ee8921d3f9c97bbf9e12a7e3f605cf972892be5c79e8a7927020ef9546603630ff5ffa15d5b896bc807634febc0a18c94b2bf5046be3f6d02f5dd24ad0ad1259f3b21af8fa24b7ef51ac9e3a9f8f18801f20a1b3254e42db67bd3701eb9033a0f5b3b017a42ac73be087ce7b703bcf50ba9fca23b9f7a9386ec8be5d8db7daa421f77872dfb6267b664e5777659bbfcdd91103f0c210c7a37e7a4276fa602fb43c4f6d7cb5ebc59131b87e66730cc09c40f7dc2d87ee9f993244508b935979da684b99b4cf6c56e977febe8189db23195bcf2eb6ff7212b301a22cc102e4c3abb409251ba1489148bc2c4916ab18f4908911d1f5a6994a71866e5284d3a2f4412377c0c084b88a6bee22ed6727bb02c21a4803a332e3bf92045105d6ea843032a4c4f540075655c765241d4dd649899b9ad0e615f3fa2383f9362f9fd5520e666245876624ac224e4769781f8be22c13f26fd19cda71f51d1ab3ffb726227343f8e2ef6ef15a47b155b98b4dc065c76628aba9bcc2683bc7b978b1df5b5528551196eda0f695ccf8e1efb3efbf5ab1fb759e771cbaf5547c20c31273bd347b2add899178a3d3b3b16cc711e9abaaa4fffc7bf1551bfecd38e04ec6342f3f57724c7ed84aceb04c4956b80cb4e40d89065bf796111d0f65b9491f95a37687ee64da0f919ce0bff72af9a5ea8c29d2aaffba23bbdc73cce0b736c218deb34683ea399b72fd4c2cc1315bdec0765feec8466bef9d6eb578ef3e438f83e9fce19ae5c889f89738d0819c4624f2385c341090510602ecd5b2097e6ed6ff1ae56565eb16224161b1fb36fd9419adf6abc1c310c5b23023b58f3b1837645e3d9776f939cccc2b6ecfbf05fbd8fd5fbdb0f87ffea6ddea6566b59ac7ffbb12bc17ff56da023c13e0d3b683d26ab5aad65b17e67157b569f94b2b55adb92a9da072f9394485be43224ece4669efbcef30ea8fb4dd6d4d4bc087c6bbee8d278e15f9a577933336fedd7d0fc8c27f3dc591a8ffb19afe85a19ee55dcffc4ce630799ac2a6bc5fa2f04a1ee8eb42e8382bafd08191276de505ea720b65cedfb63c2bdf6454e06b13288953f0214b871f6dbb6bd089c437be7ae356f7ee615dd66c2f9c41eb696c5f2d90163aa8b1d19c47a759ee69e562cfb185679e4dfcf50b1afc1f687b1e32777e2931cd63b697a82499219f01084bd10430616be9062832870f0a28718582d8509254b30d40004101bc4f084c8901d8a30d165090f2da88162350b7890ea4187c9c316b59578e8a267804056c5ce20077c5947fc3c3e642e2dedf945f333cfd7bee6d178f689aab1bc1c07297d4ab5eda31a6d62fe5e877e2770946c12f5450d921ab629ec0a4c0b92201cc757f889a1302d3f513454a2d858a753008a8fbd1a8907f784e1b739ce539f2565a32a6877a210ff047d777773cf9ab6a9bc7ec978cd6176b711dc2cbb998ab9c6f6afbedbc732cbb6e9f9d4b1a30392df0692ba34c7a89adf8ebe5bdc79e4c79fbddc89e4c6fa1f592cd612a5f374d8351f7376761a43d47b0667d5b953033cffc66f6af3ba24098b615a6a6093391137c6ccfdabcc924ac311262d041cc230a283306e4004104548297041120e516c2142c31118688ed880b302ac090c1557c5e6b2131422599887b235849f2026648c4cb93146492c49c4c8fa31ac9c9f48b3cc258270557e82b82904d61537becc20106fd0a1c98a2352927e64f9228989192727702203154e46c0000504839241a687803244461797a60a2338ce0bff7eccb3e7c7b90721777c1ea4f48b3e2de715fbf985f2e3db929b61933b1c641d0ea2f03cb8064719e79c1f3ff9c7f8135dfd5d1457688f4848962c258609718d99fdc4b09fe8e2cf3c3a299d13fb493d2ac1feb53fe01ad84bfa33625fb9a73ef685f4a5b459fd2865fa85324e8f489ac9fed427636f3286524af7f6c2bf93e5e0ca41af77baf59e7ed1a3fa49075f7ec8cfdc485c83bf0eb1a1a4586c28b9fa91528a79b2d69f539763adb57ed241afca1b23b5624379635c81113331b8ee426c47704f744a29a53b3e523aa5f4a5e59ef9989573cac845720577cfa2a9f2073b3e493b3e3bd1b1a0e20ee09ac3b4ec60b9fed2ff595f5bc035cd247258cf254915478071c514305034beb01206981ba430c28a120c0b1e3a4a12b5c288179ce0053488e1082b17b8226949144034f1c512583c712c7e50b510412c85f01364458b28becca08a21a0ba08808a2a947031832f9618e20a312616465a07f11314a3118a45901ae315606ed412c7b8d31327d7ddfd75683ca1e149102c065f9957979f3fd0e4b3636464f463a7ffe9a9065b11922b560ff68823f6e6b2d30f4020616b2e3bfda0c46760672e3bfd30430d7728425c77284a5cff9ae33d52e73a41c9e26e979d7e68e18660170dc4ed2c4afc1862490c101a9a5e0490c10e90c090e1892aaac802fe85c0ede7dacfeaaf97bbc6e247b33bca6d4772241fe242fcc8873892233d690dc99f3f3f2207c02088a697fca2978edd7c4a56e46867d806b33c1b8bd8b0fa0a6480cc97cf80fe4d86feec5c737337eb32dbf838fb2d55b7ceeb97a6612a0f7c6243ecc6af187d0cebdfb4c92f28f742e57a97ab6c9114ddbfb05e292e9f035c23561b9ff596159dd890b1b8f17b89f32c3de100866ddbd7bac171db175dcdf3427fccabd9175dc7e6276f33c91af8c5cd4281dd2235a9c9a0d803c2952f769eadaa3e64ddedb757795af4e26fdbb64d1a395ed9e5df7df641709ec9fa40f575de0bff82b2fdb675105fee45e0cb7ddc7bfde95cda682a6962d6382eccc219ed0b655cfe6676507bed5be160c3152bf654e79146afd6344d7b8f8a3dfe9ad657fbd80169d36507b4715feb867ff726f8771e75b92fba9ac76d5ef58aaee68539eefc9fb8238bd8987b302a77f8b3321f5dcc31fe951fbf50fe94d2b1c0c2448bef729531394819d9c4ed6672a886c10f4a8a383951440635787512508a686961862dae7885f636f1802658c60a207217f6928018637c0e1bc912252a7490a283951bff5350c38d0fa6a0891b7f67871e6e7c9f9f1dbcb8f1835cb8f18596d070e3e3c8b144ca8daf63090c6e7c1e3c14b9f15588df463c38ddd8430d37f6b0c58d4cb0dcd8b1e7129b85460c2c5ebc80058c292f14e930060e4670e1851137888154c1151f549cd5711dbe3d1d3fa11318b9e04b184672a022b50222c638d102315924f1fa018b93196eb52391d4d92f0ba5b9ce375ac39ebaf537f738af45bfe8c57de7b5e8270464f3ce7329cfc34457fdcd63d76a60f9c53d2bbaea4319722843db731f518daeea5fe852d5bfd6d0f6fe5b4bfb4007eb7753509e20a2082423a3d74e74554cca7e08f6b814bbead75a59d50a1627d7a5a65174d5ef2ad6ad805281514e5858a0610617466444116169418233e5c80d132336576c18b2ba0284869a1c18a1893232c384752222230411550f3374568ad0c0596981c8c6852645912c8923b50824ec0911ea64c391e9e4021259656ace39e79c736255bacc39e79c734e6a015043152e00b8ecd40407b6bb488da4c28a4f46d50179cba04a901be3771914dc8d38f0b9b1c9871b9fb3206441430aa0f8c20a1d76c801175096f0d284e9891554a6d062666b175c7fe9840b9208808b1162dcf8f1b10e2856e72f369852493a79d8c60727103716d1e5c936a33767843b190183eb5a28a1258a2dd9141c672505b38f38e7899fafb2d858070407655015db5f9758f9ec1db9c9f6672db0de91ab94294bb18b00f68520ce90cde37c44abb791372c1c9b2fc4beae3c2328feabcfbc1de09d8274b1cb4e538e2e775ce74dd7936e7d1c17bc3b4014b01bf6fd008ebb03bc18f756c050b9322e5b01a374a5a83ebed4142e539e2247bdb04b5d2f1df96b85f8eaa8a6cb11cd7ce10e1024eac2fa870c4605221bafa33a0bcc9079ee2ad07a9b6f054cea35ed47d709ee59ad75e7665fe8f5b5ed7dfbe885e08d325e568bf34d32be1a9acfe6e3deb63e1f386ff316e017cecb7820a880cdcff88870dee62bc02f9ccf29cb06c60e08eb3cceeb41e43c51d8d7b8b8f2e56b5e08de08395821f9ee9e45b1361fdbbdaf3c755314162cf129a34b157b5f2982826559d5eaa340743115eaa51d06583d629c149017fbecabd72adcda4fce833dd9749383fd84fd150bdcbccd735701d6fbcd475401105400e7594dde535ff2fbe9c6cd1bd9bc8cb779ef4ae097cd7783e3e66dfc59decddb78ac776fe886f546ac97f1acff8952a2301b0f8a09fc92f19be46e3cd6c7ee86cdb3bea37e11d97c2fd87cbf8e6cbea37ef920818ce7a6978c978cff7116209102750b70d98905576e7716287ac9f88a6e9ef52c8f88f52bd85400e76d3e1f368ff316e097cdc79aef1e7d1cafe8e68332dfa652e99e8d4744444464f3f4e7abce4721fafc54275cb69ab22e438bc98aaef92d58e20ec8d2d982a4ee3220a736b5a9554cab8fbdb6d189cd39a777427dedb7ba655b9665357baa79d857ac62934e1c36d6d958d1c56ac19175623fb4f64a9965167bfad9874f02f6d891bfe26f544a69a364bb427c45ef040cd3aa566ba58f611f5744ee4821920a17e71c94ef1c9b4a986d4631ecf65c53232316a45118767b23262614b02b7c056886c035e49283927543bed25262d6830d9b89cb922534880ddb28661d3b76777f2f7150f6130765111b3297255446a3b8c5766f0de4c0216554d1ffe80a7780d7b9ecb10cd3304d7b11f86a5fc4b0af9987bdaf1c949e3fe719a97efb4dcab8fc52de010975ef5e5f95d7fd16b9d909a97ec39a7a4755eb46f7aa6fdd507d35bae4e53c7ef9ad63ac00b886fcb88604d2808332877f38bef81b6d578525b81eaba130c7f962a393b58cb696fefa4a2b5da14f2954096c037bfa4219533f51bf64df8d9b5dc584fb9a6172a31d900a0e522db3b9b4036a09c937815ff2bbd12fee638db19d82df90a16e6e25ca1e7bedc3eaf578c1668fbdf6b303d2b29865bf7d5f2dcb7a388f8679d9d78a615f1f659ecb867de5d2a74dc4a5df4af47b09fdfa4428f6c8ef00eca25fabb50760f5dff00097d203700dda4b1cec627be96b2507e98707b894fe8671ecccfd5ba27038283530e4a0e4978f5d715cd411e6bcb3cd97df977e675f2c8eccbed81b0c48fd0ef3856d31fd359896d2525eb396c57a6a29732728f6f4abd62fdbc1f61377cd9f4f1c9c6158ca14c5c1b90226f5aa469ea8907039c208a39f7dd8475e1bbd60ebd3f9d9cb0e28abf5b5ef9b796dd4409ce7087b8c7e8d448883f35d366c21777e83719eb036394fd8603a8cf3f4943ba54cddf94de5ca9dbf4d2efb6299c4fff1a0d853abb52cd63f08c66fe7dae89a4dc541296c37b51407e7ebe0521512b95196c426848ddffd4f53942e157a7986bc3c5f02f2e7cfef84f95102fef20b3bf72ff2ee271beb4caae320555dc6c2b684e617bde4fb501f2130bae8cb2b6c0e4ae916dbbd41f8a1348c846227fdae55c27cec71c8af982a2c72da3ebb503221e701f0919b61398fcf786e72afa338289f8a51104b905cbedc139f79e2c7ecaafe35f131cbafbf61368f75403a78b0190fb6f511f5abf5fd5be53a0676c967fffac82516965f2f93e16037e02c19abe71adffe06de13fc71de7e085e6bfd6d7eb3f136d082ed8bf322e870503e4e09fc9ae16fbfc63bc1dffee65eec44e831b739ebdd3c77227c2a24f66fbed06bdec67a385fe3e13c774038df0e1b6f030eca195e4be9a6e8923fbb66bac2862de5654acacb949417290cc3302f521886612fe3e6595fcf8c6f7b19c4666e34e5a00453f3375f586f8dc6aab98065272a4a979da8202de0b253115aee955bf3ad09f8d7bc7f354144f98938378ff32c2f2cbaacdfa88dcdd7bae1c1cf78133cf8195f74ad17d6f06fcddfb837c3de78329e3bbff18aaecbc0798bf3355e98e3e2fc4f64799ee4a07c007844fd02c0770784f33992a17fa27e79df0a01780f7edbba33815fde4754f4f27e86e7e3fb19d13ee7c1f7cdf88e04ff9a00bcbd799bf78f89f7d123ea97f73fd1f3f8e581c7af195e0bf167bc8d17d6bbfaf7a0d47c771958e14554f40ac0d752d1e5b7e63df0f8353ba1f87174adbe16e360cde7401c6c268c662018ee4801c3867db4c5e8f28edc51e422c731bb6872e6138bc960eb9df9b0a566fccccc13f56bc6d7b2cfaf191d7bf509fb2dc52ea9fa3e2b29b8f42702c06b3de7695e0a9d79214b85b66620e76b0dcdf8f8333e7e44f15bdffa1f97199af1adaf9daac2156628dd388e906db22b76926b44246cbc3288eda41b91b0f286b256d63f184606b12d0429ec2423076303b9526cc8616e6c24377e0b09c3b20ab1c73fbee277521f21dda8c45d9083f1739e95f3ac9c67b158ac9c67b158521beb541f2c8c05c141f9d8f701cd955f53b0e21e661a768034bab2e65ca9c3346ef6c5d65c7eeceb536fc3325abd8c529a7d21f622a053caee96be03b9655f2c8d532fec4e74c5dfdc3ba0a12f76e6f21741a53b20d089e5d7f6fe19131b8237f608d1af153b81bef6d8874f027d2dec31b7cc1362c132d4d0ff38c8cede6366da538feff4b4df26d7b3f56c94cbbe58eb9df6e110f2c26e1eed845831a08ea8248d5655492c95a2110100000000e314002020140e078562916044a8aada1e14000c76a8446858970a435910c4280a6218638c518600020030c0006310322a0324cb51e12c7957299fbba92281a2f732c4485a54fca79ae5d84edaa8f8bfbae51e3329a2f8f8abbf8c5a498e02fbaabf845805562cf8468ad84eaaa8e8bb8ae51e26e9a376feabbf8c590d2b2ee2060ad84eda28f8bbaa25c84c92a81fefea9775daa4a668dddf0c34186cf53f8ffb1e85ca75a0be6dc56da23f10a653bcca41fbffc847f9eb3b50a36cb4d9279ca9a090fe4f56a3af0097508bd5a716774adcca11413fb51bbad14e88d4d00d6d2afb626d7a9de07d5088b79f4222987ed0349b2f9b07ab51d9695008d85e4a044077b2be307a916b809193da3da4ef7a289afa73138f18787e339b573f0d4afce5f2d210879f11aa978233e83fb6387e79c3e129ef7e9ae877770c10c14931231063b1bfead04dcff7266d89da5a66c27cc982d1f8ba5fda16c289713ff33268cc54261e5c80ee1036da63c31e21d0c83553ef318634664705175733c40e9c7f96a68b7705dda3dc692db90f5d04a1d346907b14a496f243d5fb3c54d9b3b768aa71e40e452779981a4c856683c3360eb0c5afb3185ca9712700a65023dedf14dfb3fa008bbc8de353d7e504f46f84718b2f46e66f5164ec81b3cb6c95c2796fc52f33b2e452b267b97bdcf8ccf058a230fed7c2452862516ad1790bc533e6e7bf448483959502d195c0017090b1bae0336e7feacc3e011c5f14e9cc2339bd9086bf89d5c496064226490f672be4fbb037b775341010c2c4362fb4ccb4d7e8a1f1edc4e641a5a855ddbfda49972869ac7a8774800f9f221a4011959f28c701d392ea282bc6c13ea05d0120f3e6dcbb14b0ba27ed20070a61131e2c81834cc845544ce94922332bbed4e41936023566ec310740eec7a7643924cd86c6a85cdc65fc1bb026e91a5d17fca86440f644b8fa59dfd7e09ff8ff358d67bfd90399b3424d08c22c6372194be3049932a2d4108421bbe8ae3685ef7dcff47321a37df8825ac2ab701c88219755f90c87b8fab13845b0e68b20d1285a70102832084e8230da212bd4b56c25ea8fcabcade93c4aa582653aa708aefec1e603130055afdc0c36e935885e8ba0d541d3acb49daedf1c7b2d6a20ef7e15fded557df9cf0b49d8a768d73630aedf8d728c486bfed302e362308b172cc6750a2a21ad0b89865f305b974e2616e11bad2b89952e2c67379db92235ca74c041a0d5019243b16a531ad2d538d0ca4d3e0d28e476c0a0e4a6747ba21edb1460319eebeb84edd1fc6a3f02254acbef87f1ab9e15afd82770ee849902855c91d7e4c6ff975e132942186c25fd0886293458c6bcc49b32b3318e88893bda7f632f572ac5cc90b3e6f0a0567e7c9269cc8cd9b0b4d76fe50a428ab0267fa30efc69603538171795c9d7960d8f027f3f4626e479e5149f180bb90cea1744068d116178a374aa673034431297778f980ed27402c7d89fe2ff8102445c1092f54266245dcbcb39f019d0171999f042e188150008c1a41da5e7090c30fe6d122ab318e48f11b22e4dc8aa895c52e8f789589067832280a007e4af742f8b5d46e87e757db860020de9ac590a87d200f9daf8ac60a1c64b42042e596f1a03ef9626e373e3dcfb7ba78781c37fe1d4ccdfb00fff3784eac1cada90b743def7e67ff669711de2196bb12a75bdb5af5446f4fa6686a9b6603ba84a8670447102dcb96dac1a5f0700b11eafd69322fddc05abc250517341792f68656d925480108efd22e7cc7bf3c6f23b07172daab0ae1accce69e866782fa07f5c52008bb30168bd88981fb5681be668770712b07f870e9b460301783fe28ebad506a9043a14c06fe9da172e0287bf0a29346e203632b0bb983c4f97225b0326a14d113d763003c85f8f572973fb2fe3fda4ff3ce28a3bcd767a60ca840aec790735a8d1c566734e89c508a51d6633795df611a065ee128421b497bc3a91f811881886fd39e2b022dc75fa25386d259af94634603336d5e3b43f6abedc72007631ca85ce9f50ad031b45f88417153d1efeda52dcebdc7e65ca12a71510f1a11436c8069f37afd9fd5e30a814bd6401774eb342d5a34400fd39cddc1872fa6e48328d5538f4708260b2da009a69b8e70ca119cd0d55b70784c84321f561a0be551d3a4e8ebd3c5f8ccdc7cea85530e5de25b0f4657694020b4d24df0e7e112b453f4aa3f5f0dfd5f16f94feff4e4ea2d6c1cdbe1270e8775252a023f6d006557eab8f9522c96544d803a403d8ee8e9ba77b761eec185d3bc7d50957826db2cbc79bb2c50a7a83fe407b83facc6850f182a4a04bda9f420451fb0ce709b2d660aae7e2a52dabc272524faa49e7934ed3b360f24f88608270e9e0bdbead3e6ade169d4797273e19b378b291b3c0aa9a855224485d8b7926d4ba376af96a8923737f14a2f51bf168abf99eb8e47786e68be47feb18f29de00f336029e4e40a21f5583138b92201593d8649be19961a27b372f1b04fc67c6a2789dd47292266a5e1837dde06baaa18230bd9db965dd6963167ba3aadbdb83fb5ffa764faa3a41ce5599bcb6fdd4dc376e63eda99b2c96724d5ada840f2afb49ecb2f8dc1b944b2ae53a77faad04b5b39b2ac27a55e52cc3ada2226840bc4221933a63530ab022a22114c3b87bd4810412efe46484fe3b84f94cf67a0315ab8d94c400ed2f9f042782785b4b911eb6a4556f3e029de1176a6a3961df3e0e9bb13283997367a465cc8e75128b734ac12846cf34f6172354e9e6c4e97a949b991264713253c7715a69e0a40f34230ded049ea9ca80eaf22708d8a5077ba4e65247539a5eb24bfa36a93c1950c26ab201ead99eeeeb8028e3f3723474f52908adc41391db2d4fa114d3e122162768905b42c592276e2cb81e26f9c91546d7abb45d171bc6d58232ebf235a042fcec78cd130f992930e66757c259674b2971e88f55f49ce59662f2c47210c85924f14a557b6ca4127abc638ec6c59e03676187ef27d239a2906565be45adb6b2460a84d3333a2654089c106847051d1bb8138d2860af80b2b879a96809c994bde63ac5c8a90a5b503cbeddca4eea58891b2bccc451f30220c2e21902a50f8d2e0174993d0e11799799882f09225d0510995fbca74945f818dba6621953a9d619d9c625909f3d42596b829300bd9fe35a313c27d60472829c67f0ea3ff827722b08b9285f37ac1236dbcc1e10234115f86f92aa98166f29f57cf93da9c3f77df9e36bbb3e2344261e900efc86e0832f8867077bcc42786e212b87bc2991ec988c818249a25586a0a9f67f59ed7748bdabe058be73c0a4800b7a8234025c5fdcc9e9f8af84c8305caa1a53d4e1a127e4299801d2f7f7a3bf91df36b9333342237e544031249316344e1718b0e1451d7c8ac0506d4ad227f46c62aa6f340f7f3ccfc40868c943530c4b88a2cc4e1fc52896ac63b3aa6766474675358043ae7dc974ef92e36186d12e6d7acbe7d89283ea44aae3b0bf80227245e9a402b988cb8cce5a6e2aff87a9d75fd30b03523700f8fe3b721bd8db36cb6a7e49b5064320967fa16d68404bc879550514124dbac82094291a26982f664ecb99a4c0ade7dc7d0f3d022f00003e4c6bf5c4256882aed7f35f6b6122f4d64eff072390a0d4b2764c8ecbc80b06549426437793584d97da2d0ef7c579a08c6cdf64792c8b10d8fa8b255db2e3fb284ac39fc630467c9f3e6d059a528bb8c14dbb0a1198a02994073e6237186ffd32bc6b7aed479eb07176dc6d6ab2bda34a0c42edd388d3f386465a6d9890174cba81cbbcffaf177ce6b00c9a0ebb41026169a6bf1aa7ddd850744c6f95bc90c0a92eb84c773d69f95822d48a90c813cefdbe0bb0f2d937ac1d29fade2145b37047c640448d6e935d70f0bdbb7c5a75a4688d44da8cc3f35629abe733e06f630a893f6f65388303625dbff1e1c4018dc1ccc28bf354cbe3b1906440c198184d30d18181b7d4b4bf7f4de95e8808cbf45b51b70dace9c8c89004f1346d119cfc1e9c839d772d7085bbaf38b6c51b75ef4b5bef7d6a01fc929ebe62c3c99658a1a4b913b01ea075b48630925c12847c946fe2d4e875aef586d39e4cb9fdf10439db59cf2fc5549ca03025b3d23f07f2cc05bf31884e87658e67ac087304c2e2f674003b6ad34c3af219112c3531951ba08f6e45d505a7cda2ba137023e96994b26553575fbe111bfa6146cbae68cd36190664e81ea68862e9c0bf7f34a9c7b0fff9ad5151dc372c66a5f144d2fc757e76fe0a6dde06c177db7f55877a53996cec64e88468212a973371b10fecb6e08f035dd439f9c2d5dc85930f8d97174a034bc11d68585ff614cc392734bf08d49393efa683e5e319c2f9fa57ed29ff082e3acde3fed15877dea0e7e41086640a46ca933bea05612c5d2e7e6091d22b08890a68bb87c1f6150b3bf6fe345509f8195f0abba196b077bb938cf9020c363bb8557856e9278042a4eee0bba51aaa2e845d899d8ec8472cdbdf09a21a533d0ecd3c2d49c95dad0aced6c6401658019d7f26a18209c72b3505d8166bafd30b78b0989c1d1416425c084bc715c572c399e7038d1848fac7360dc16748e496f53a771ae7b15b314f8cd62b4fb4095824a6835391231812af17bd5cd746f85536e9ca0228e423c90f2e8def65824c9a76aa1d3f00458e948e1c352eac295368973f887d255b6bcb8e5a0566f05fb302becdcda25ceacee16c18149739b63f85f66e7c2a6acc8b1f1a94e73c60c700a5e4fd89e6323ea513b4526e1629451aa51cf98dd6f03ca8eef3b3506b79516cde796e9ba3896eee6c5116ea0cd6db22c9e3a65b0e42b932e6d09f2bfcd14dc7cbe44d0d748e1bb24230595bd5c4e209de47faf82224a20451f6b7a7b5ce9eeb22d920ad9b1323cfa201fbe4f035f6294363298016ac5c6982a1dd9a02596f1ec71c598b98822908d20f34248014d05dfa2d842f8500ecc40f14c669e93a7cd8a1236103ede70559f0a0d5017a9eb93ba638e6243496f54187abcc59ae1a3a0ba1df76c147ccfa155abf520b3263aba27e9d52b8e8d9393bf2070e53f011ab38e300bde6d9a1351f3cd25e271fec71c6f6ff271174c34829663d3cafae49250c8e996a82bdbc8d51b876146f5d9eff853f44b90db5159279a33dfbae1952e9a74a918e486f15b30e3e2bc0fd3af12bc960b92f2e2fee743a1037adfd014229a20fba4f921cca33ef12f1f1fb853313e16adc3d1445705d4506879911a66f0f6b54fb43f4874a8bb5dae3ce5b3c6c50044cc1e1d751942ee5d3638644a348990a341e18a27a6bad95775e96cfcbab4810af0eb76a32c4c122fb48376ba897008dc8f60524dc364d3746596e4c88f990693a9096ff2a7bbdac233052d4b37d5f0bef889f63acde59618ff75b482dab7775e1dd410482838cd7cd60b7dd2d6c1e50b470dec73e6bcd75980359f8fcd3e30f409b6fe0341db0ad1401a0b532c088d47267033ff0b4b91251ec31479534ef7ffa93a45395868994054cd676aeae5013c55891af5616982d0e11f8e26cd02ece5c9085c40305fdc2d20ae21bfb41e7a3061f3654c8d715cab494084cab0cea5a2b864919e3ad0cb7af38a620e0bc3160400f1f81f552b9cd8a2d2a361d913e2e093b9177844b2bbe2ea14fb059f97fb45313c016bda8ec76f4de5bc76bdf825ce5d9716f6af1746b3d234514313f6437314ab2faa594c4e85e38492116e34c36daa18efdf8ae3954290304adacd99bb87659d09dd5e316da5dc2c100d98656f841039418d9d4f4fb06aada4394e3099ab2799d383a40fdf56edb2010c9d2daf524eb8d8d3e83cf6d88b003da23b986432e07886d249d3eebb45b8d3d279d38f2b095f9f7a17e469dc99467ee0c00a3f04fd8b97160b977b3c1fe8a351935acd803c9cdf50914d5f22f6cc5572e961254ecdd0cfae8bb36dd24415d208eab71db34bb9fbad0fa833af56aa8672f9f654ea6f680b78557e97059f49aad3cca62f0bc19a73f20f1414ae4201c0ddd4e7a850cb31a9e09d2eb739f84b2e1ff2cc6034517613cf73cd569f7bb65c0b93ed369adce31c830cb074517adfaf920dfe276b812575879147f49e227d583a251293d76a81daf2472e5afad8053321cc907d22df0a8ce323440e03250a2adfc831a58e9b64ecaf6c462bf483a70b6ba158c20c5cb4b50fd9c36d6af8e620500b3ce7e5e53cf68798033525b6dec383286c2564573a743186f3b4d4839f2317131efcc8f46f533bb8378144d14faffd336bf6af54a7a665a30c2716921ca66ceea4dfdbc4303f5aab68e8f2dd88364f85c4d0ff60dba2b16d7c1d296dda5247daf877096182489458be49827543d8e42c6c8a1c36589a54cbb466f797b24d45d7c1150661353fc20d2f87ddd628b14b401301676de6169d15c992c0e021cdce410698f0caa02e2eb6505d0e6beb24e05f74a00e364c486e8eadc58a47d62806014b22ca7f101db984253d3e909e1ac983283a3ce11fb2d1340ac6502973d52173fe0785c5f13d48d8d3902cd84ebf58b2821dd5adf06a2e5ae734c1c0640ab65cdf2245ff4962c1d9ed0277a4e92591a64b7dab849244f3070dd0daf13703d83e44ce499ca6a10be174fbc6d3ed1cd4b80d33c4caf16acbd695c9ba734c62a56f8ff8917dfa093af8a0e7de41072a2ccfba3be741e68e0715023582ef8a75207606114a7240a439d061aab4f9ae4974149106ae8780edf22291ef5bed9ff743d1452e9b38601387cbc69dd5411249c87a2df0e69d376f0c3cdc91c024d9693c129837b3d24eee014a35e0de169c055a6a1e9693901ce85199a807b1027fac1d947a302ca955efd2d07f428d7d54d31a5c08df594a7a97ecb29bba1625e9a517a6e32ba30e61e22a990eb4b483b300dd0123e4b6843db26f791153a339b14af4829bfb7474e12aafda0511066a86f7f1161c0b3cf12e8aefad0c6760c80d8d9e274fb2c869558b03965046bb3e895685cf2ea2a57ce6ef7a3e48364629ae3825ad406d05a67f94ee91690cd0e22be56bdae2a985086bee247b1a97e7ab8e87284a61f8d2dc43ae94d450ab8b9c8aabfa39d50a633f76f14edc8713fb4c28a041e56a7b4997017178eee9b519296ca779965e7433551a2863336d5da31d296ea94a8aa8005670e4466b61e283a0031ee1a5e83ff9d8346e8fbd3cc8a4150da4d90d888ac42d7602bdb2406fd2acbd4e5bed1edda77ffd4cd042fd5820858e9e94e3d32e66393aa46715be2a6852aad9623fca7ceff31029064117b339a5167cbafd50379fd242acabe78d3e454762139d19cbf9271d49f973c7e87f953c682b770219084597cf39eb3c5869dda466712be24a3d0a1eb5e1c04d5eaedb51c06cb606c319b668561c8375c35b199ac88a2cbd5d2db4f38d91a35896a09224631f01b68942e893bc3663c36cddddf10b67125f30caedf6077c10a22ccec86cd896ca05777684746b0d2bcdd9a4e1f644ea4df3996f168d4d7f9c47698400cf85835d1cb37412618ade890c95687e34e3427c933a81e7aa9c59435c3d718756b59be90003c2e901ee662b69b1fa93f8bc9b50b1a89542d8a4c9dbf8c3a42ffce60deb6e4acf1985d9fc70ea8014e20f0bf389558c47bffe9f2769c07b497b0849f457e1473f25aaf111517d492775cac414aa8bd7ca5bab8d35dfa06da5f19192580ed4533d91282063995a616c2bde83074b2b5b59eeb242c29354bf58dd48766ad5d44db2f74bd239d34af5d0e2e6b957e3788676fb1620a12b54c5cb2577c7d5bea47a64bca0326fb2e264ceb23cb1503b3603649a4b9832ef9b472dd4a497f79cdbb05d0b871d2dde197cd50726831b2d94821a1946be0a4a29f0a69889b0bba55bfaa3935b4c74f95200f392fdccd2e1d7ac3f6c121250b87a7ea3a89caca9060ae558ff08032d11746c0091a6206915a6890621602438b0fae97e0fa0e993c26c7e3037a10a90eb22d680a1472c72dc02e9c4f7fb0a7daa56535fbed4d126b3afdb6ce5083dd1b16e7f299b47219cd5c483a3f4669334cd1dddc18ca220482b68c41127a673a857283d31c5d43d3222502d49bc2acd1a3a12aa532cac94ee9e78cc2b81a1aad688864aa6682473b7a4e73127f2d1aa3ee5d41f3478d9ab6c75e5f24240f03780ec4b427c843a460214dcdbc275f5aa458511a6f0a4e504b9509b78eb6531af4f218465a9be15426ee175cf2480c034bd3084b0f51c652fb8abbca28721b3e46a74b8059c7fb8d8f6ecb029d8b4edd361d00b80e47820fc9e531ac932bce8b47922870866c911a588cc68a837d2443288c17007a9784528f4eee338a06adccc0177764d84f9095448a2cd2bb9bfd90aadca1c34ec67709939b7dbc9e16c9efe76d29cdb120098e482503583a68b9736e97e6640a09cdf05c4604f9fbf571181c9f2f705bf4bf6c43ce70407e31fd454a6cf298f0296fbfe138811c800f8f44b46d81c8ec02c20069af98139d24060fb4128b7b208a47a7f532ca4af1df4b85bc5e3206e6bdd4c9f5c20f4565f134c215870e2695b65fba5811f072a331d805ee6a64d4761191d24d2c3189e6b0d4fd4ccb477d57c202b9d7425fd2aa1494a07d00977f81da4ffb87bef451d311ae4eb361ca78563e3007921d08460472202a06ff842c70901083093ceea59a4b1fd345522ddc4e7f9d3f9be5760ded4e791b63de68d2f06a86d9937be23338db67de7ccfc34c8543b50c4bea0a31b8411587fb929bfc3b5d775f50ae202a41411098b7434279e057909d3ab2f52e13bf7a858cc4973ff68a49eea2740bc1715dcbb76639fd1688004d8e2d8819d8a7b2d26c0eb635add4ec2e9bec8e0407607d56b11096af1d53504da9fc2229081fb5d11f9a389868083d83b3c8d307e2c993f3440a5221b650958e97b6a0441116d06a0174a4737e76959726284bc28e9a6df96268983efead83862d2f846194ccc2a02b0bd71b902864fb8ea7eac9fd87c376409cf32f332fd88fd9306f5234152b8ebb60f3f591fc611de3883573e1f97398b378cefa152a21742cf097c90b4f1f91c96339562307f87cc1c3237577cd8e74dae54f740cf0933775953097105f81a928271ed6c451d110d03e9ac70a088e50fbb7565a714b308d7e937d928917e8caa6b86677a4e0398d047df69e05bcf0dabf83b036f1120706919d9caf5bbbf794cedd8d8bd2b2fae7c0bf1e0db25be107bb402f1699f730e956a9ee6316f71bd283f1b52c147dcac1f9cb4a16a4cab062bb1979dbd8155027fee07debfa967c028d63b6f5ca58ab52b72d81dbb88ca3bb9224ded7ed0ac884c0a69d8657c9403ed30bcca5ef58fd884dea2e8a9e867d9a8a609cd9578d859d53850526f4fe6cccfaefc91d5886c4fe5786e85c2ad7b089868001158ceb1a4b39828484bbe4cc6d3141bac259a6f0bf7ba2c992238832b1df3ceb74c24272121dcfc8cb511d5e977917e4da72f0e64f415ab1a2a3cab3e6eae9c700e4b381d3a6ab8772825e7163a42fb46bed76a8824968d3dea1ab068bac3cc309cf28d72986154d851d13ffdb11bdf980a4ff5fe031ea526af8fe677269d09eb7326f3c5912aaf67125e3a699219d69e315d235705fc9d35286cc4ea1c9b8e5c693e1b6868f82779af960b774b77b5f2a690fcab4800ce4b650480134eeed807cf99bc3f810c4a12604774951e4e4c5b9ee0fca071d29aa2a031fc65026913c2f82c7498e9dd1a4fb7220595c6138b66e94349f3a826a5893074271a9aeab203c16a54f654662b5ebfc11106df2acaabd1b21e61262a164ab0e83c580e6a47ae617e91478ce3221589668ade58a3d46f8518738ee29dfe4e7125bba3a189487a6d68042765232b46d543e7bd7ad9d141862ebfb9b6e15474f61611628cc2d6c8f17d8108dca4cdf31ccfa58f042bc3b024b1ccdd0209e36916abe51a23589ba2b430f8614d1691a16c0d9e93b8ef45fa1e539f19e4f005cc245d243ee7647e5f4178e0196420527e60b7761cc7bb5e3fcbd63167ea2888b566c0ef04efb5abc0157ef12c6e7c6435a21bfa24c572e97960c36a480d6d3d8ff8123643836952a6ba825faa289a633488185397e063a7b4a7ec2a9cc2660e1ae131a38a2099edc935cdaf0ad7b7e18d33cdf882aacf7eb410f0d2a11253aaed4efcccfa504515ec424307182b2e157a5543174b993ee1659d8a81082111363624422bcc32f7c0b5a84263ae7a1ff0216440955e0ca9fe19a3566d30f5afde85e61e965e92919dcbe9cb642748ff64c5153c897a8a936705945d06d6be94a289a6c8a5e764ad3c0dbaf0bc151a5397f4759952d29cff91dd1e27ebf9b1c426425ea73af94a552324cced0d13bf0ca5e72e673512da98f3cf3bac01eac83f3fd3e9bef58bee16bd6c771024248b76d2ddd5c1c94adb3e8eac763b2099064d3603e0a5e5c6121d648aabe8f26c4dfcbf15db8d8ca79b2139df1e7230cf16c9c62fce56610a642b641f8b593f103828090bbc7b89c3a5bc6b05713f3fef3f7c0360b4439c78c56e3651d7dc4f8522f3021ac53d6a3fba0c59ff51f9b3d5447e2acc12d92f3b2be831a9a46817aa1f86154d10437331052bddfa691383ae49db7147b12fe30a21312234fdc3c0edea38466c018daa5b5c805537d39825a3eab68bd6eb80ca267d6f038e0563ae08913ca90a588645c2700495c57cd67263d3bb86713955f840d9d4ea080433a61409cad643cbc4886e311e031652e07502fb6af7dcd116de8105c8d7cf4d73385c082eb738cde1a020ae88591a896db762b9f712b8e68d64b4136d5d4a5150305323fb153bec77691fea60156df4fed2092a4560cb917640f819ed2428dae82caac24b52c971dc9067bd67cb6e79240ef5dd1c646ce0d0a1caf0aa30b65c3ef2c40e33b62416d2681ec7eb87e1e9cb9df8591af419ec6918746eb81d9bbcce0af21a6709b157de4a3f0f694e1b25fb92dfec86d2594d66cf8f75c733699603a73d80c511238482217e10ecd7f02f421b32d013df542aa1100938e4970a60c4297d98231e0d73707778474f3024bd6d18b62408033d9429c6e71c6393cb71ffede1ab33187684fb22ae48db5c59b998959bbcc95b3ef8bbed407a8f22ad90cae22af14b4a9248050f2eff1ce5e7c803b3a37f515ce3e03755ad08b34243ea4a802841f8944749da86f587f9425b37906454284ed6141808a9822ba34e32afb43580e58086c1a24a167c941fe1c04dcb8d20458ea41b5ceac8b062ad01ba6c65bad96b79814817e92b64d09a9db9196152827e36356226619ced7a036c81588717939c06439e3279b67272d44a8d8e77c9e49a0afb65644bf0811c3ccc52c0b8f622528c2729c1dc071574adfe2087a394f64133bd07ea281439fe3b0fcd590af8be8eee6486cea83cf8aacd89658788102df8c8fbe900cb83932250fa5f7fed8480c88387631ba530380ca6e0266a25f50466b07ff981be3d1414c3fc56f2db612b2ad9a9344d712c4373b805d91292d131b8be93b65edc4e3316028293808a06a6e5926be5229917592a6c07a4fb185336e596e00c445f9ddb913013b66737025fc57510e2b12d9c4b2b0355697fae8a78d4f4e306f848bdea069012b7d25e6f410971269bc9d1ffae3b2c95bf84f3656afeea820c0a36d3c0ccf19f2027afaf84e98d8564241a715215956449bc53e63b926c5e6f7728f08a37c28e82f5a5a294681782e441f952774f669b1985e5be0dbf0192cc4e5fbff22958bd25431ff4d05501ac425e732c32f022a2e65a4a1a85a4cc4290edf14ba180472e165e0bc75372cd8b2ddcddf2ad02c9ec882d415b4d92d0acbb5e27dd76dea276ea1441255688e361c0bfa31f8dd3ea26fa40af3fc408f2561f8d0bfdb84b9bcac14c1dd85396d22d624bd33a499ec61a092481fe80cdb05831643d9878afe16ea9947a450490538cad48b6bea26bc8f16f6b2173922377ec19d169e2b4e485b3137d3ab8943192a9acd93eecc83955c24f57c7bdd71ca159c40f9aa54775c6229911dd295d686a2cacaa1de9cf0f91198a36933ef581a49ae897960216528c52f0b68fd044258abe1033af4116071d29a19b5c757475adce3fb0b59e0e262a888cc274db2468afc7c00920dc667b031829b5d2099631a5ad993a29a5544fb21552668c96de77d4188546533039a90e57d6a640fd9ed448a1dde01d8c7898e61d949362006771d310869507b79a4aae55c51951042b099296aa6cf01c3682a83b12458e50dc24993296dae58358fbd8012f9bd509cdaac5383f32b64087dd56d0346f0b4bf86d14731f0f9d34070a2d6f16b4da7739521ab369a235a5f2c248840a9ca242c6d9a15c6698624a7de1c832d628bc745d56392084fe2b330636cef8eb648427694ccfb46e8df360c0d1ab3771a7f0f752406eaa940afa73d24b06741cc1d05daf6745529419a2df9b3b045c89fb1d71d9f7327d2ae8042be87cd07b40019108a64a722baabbfa28577a95f58825500f82257f6e100328a0e3a28a622a07b6b2227af7704c9004988892c625817701b1210b2af64cb8a0ef712da79d809e9bb15aa4276d2e6eab013f0f91bff691cd8c20548adee9385ae592fdcce10d966d7361a48071020adf842770d0d82638d1e5dd2376b31f31f29be8bab16065d2399c6c47267b1d23a8907838b99f9932ce1224e8ec00e5120010db2b09fd2ca68ed68ada50304da241b057339d8fd428d830b282d56e4057b90d6af118532554e02a695986e86d62710102c66d52e3e436a0dceff42e38eec8032a843eac5d228f7b554f3c72eb34730ac1ff29848e0c0951e2ebb8913f446822385895ad78c987a09722f61f20101931125e5b9c3913d46dc4bbd519e6831a103c1c844c103224157efe53980c812d02d731d17c12fefaf470a44c19429566a14c78cb68b8f7a2a1bb0d239f98c29eef87268943f2b251e3bf807ece4bfbf033808d228d9ea0b2a1f8fcbbe552996219264842800b476908b3b88e473b4e7914fc40b8c1ef48c91f7ba1463469b0df5893be315d7d52fcd33edabb59294eef9e1ccacfa3a71ea1c831034f2a8788822a348aca7fbd06028f49cbda53d442d6147b01893a3e640e15cd10062b1173f8430d6b15b0e8075a6a6095c5f5abc05e4849da7a1aea540989e0b6eaa2a3c2c8cae417fa65cb7f9c197f8272c2662489718ebf86995197193ad71060f4b804f2e6080c32224d17831d9b04fb96b9cb9017804e8f2f41da1d3db6d4bfcb0c3bf2b93641ee5154723d3b4ace165248633f4c0c0a3ba721fb88059f2116f96832ce3434ab33700b930044b9e6e5d8d29d201a4bede26be248c6a228b0388daa84494c8cc39b61fe46e949dbc163b4bbe1a44540dbcd0379a5f593f668864caa91580a592320cb2c16b2ec48f1065e0509f09c87d5898d31a5339ba8a7f80084c2a76c86a800f5f1688d9d0b244fbf80e94c503214389c0ac4c750f26c896a1025b99783d67eb96d800f367e8977a22fccf5d8480af4ba170cf94d0fca27767e1d3e23f7619f1021f40ead5b28f4ea4f546d915422cfd41b1501c9259bb74501228e62670c8ad9a8c58c144a0407ba56b30f1ff629ca275ccc11b69bb25d332801281ccbb35e0a94527e89f9729e708f8b321b0d81893203ececb554f1dc6ad389bce3bb26daa1e34a921d586e8c1db7ba0b6527ab3e509f62941b42ee41e4cb4ced333d7a31bfd66560c0257e08cc60a6d782bbeabef69d58967af03fbc80f633d401c4d76e64b200104f265826718e8537038855ac501575d00878aff749798baef8b3c907cd12b64a83ad86021e0dd56113f52b662205092cbfa403174c4f488a77dea314f9b40f4184293bcec840407c018049dcac1e68a433070c198fab742d52c8d0879dffa970de4c09c4d00df9f41bfdff397a17f47dae442f15f444d3a6cd9b3b1c6d1e69dd54daab95dcf7e0e7c9ed09b339637d2ccc1e235d7e7c2cf9f246eb52cafb8d1d0e307d0d73ed9b0815ae8e3cee76ee65b096e51033c7d5a19e5261a4da66c6233ff6155a8bab8f56e16d53cdec9c35270f7add105c4173d566a8bef29c8908dac73be13565a4b33ea3fc5a9f2c95a7da5617c6ce82a900a5b1d3ecdc8c3b7089a5cc96a8068081b03c046346309efc56d908dc478fb7b0cf11328d9f19bb3d992b49fc80f9d51416065c484610e69b24c2b1aa1d87dfc159f4a939aa34ad9bf5b0f9e766cb9d19236596f4b0f9e7661b2f3e603ecc8081f51efa94ecc948102c3c23acb1d1c58e94a442a7271eb8f62b5ec2064cac12d831469661512d8fb262edf800587e7f87d91db0b729fcf0214fc37684624981dd3c940b8b9204f4a4ac65bfe8095094f72b6be64e1063047a0bf45dfb3f2de7d2ed0471106dc9c9dc9166529ce9d0506166ba8262bd45101b6d9728ce3d5f16c5c49c52165b416984fb3d28fca82188819a990cca3ea41c1d0be6b9fe274fef6ba63bd13f6cca0dd062f33c912da8198b82ccc25f19049ea67017c9a12e068b49caeb3cc2f589f86ad8e560d426e5b122e05f56bb6d8098004c52e012573b408426abc7ee479eadc67e7b1f658c3e0e15c2cb098d52df8319da26f45dff30b61d12187733e248301285c34ad4f3714ee4251726222d0b5b661c7c202ba121686b979ac51c93f20600feddfed5ff6f2f4e702b71c7fd2fb1782820849d55ffee65cf45ecb25ed270941738dbe3352c69ea2d76b337fa7f289ff91fb7ce025ca343712aee2b29ddc2fc13f347efa3f97b7ab69f6757d7abe8061f0f8dd661ecf4e86f7b62cf204e786431a23ad36a0c30eef991a536e0a1ad798c6a79b8273e774457de3614a678c9e03b368faae68087fd28760a481ca2395d91e9ea90a15f7191258ae5be39f2a64ae4420d7722fd677c08dbcc04556e1a925e07b2a5e9cc1777980f7285cb6be264e025f4e5001b063cd5f1804ec0ec1e8e5868231198a1edd7d7f8ba6f2398d67574fc6528bc4432daacbbede05829b8c5c52902e6520d06219d414f45ea53db4be5cd268a44909bcd934605b1735acd3e646b61f9202024ba2cd617b7dea34d5a228d3c912e68ef0e4f9a3d3a8518b475e1626b711cfc4317ae1b54419a20739db5b16119ca69251a451ca37860e8655c0858c0aaaad15f5ad6b38eec3ff56e3dc193f47e21358dd3bd01b9d737bae7c788112efcfcd8999eb3db329591c78fa60301b27f5199c9da533a49513114e878887f8be2c89a376010cd6121708d901971ab2e03ec8ba9f613413ee5f51f0681218a20e481531ac48e89782bbc7ffc61275b8339441f6a771071cdd9677fb9a602c216e1a64200f81b2b2aeed6425e0e1e6373bd837fc2a4390bb2781b195fe4f2055a113670f542eacec7b58c3b032b03e5cc520a4939dca46b6c0c36b4a9cd8474f37cbf81e9360888c8613140cb70ff84d67332a605d79aa1ac3c5e5fc4ef9b52d8938df49cec1f8205d552ec866e2049c2cd2a89c5deec66bfcd7ee27032b727f767cdd15b154c753dffac69b3dd60e29e29f2f4dc59ce35b0ba9ac48e79dc532040a999dd3c525e9a401e14d05d8ec6c0291576d17595591678f949ae9a22be378dba455855b57a8a3ac7574df319eff1222e29eb5fcbf9d95b18c2e0e49145f23aecd998ff99ef3f1bdcbe4abdf02cd1d195eda58b63a05f79d88255e0a28d5355284a5e875217a579cd19f5180f801e623115a0e735e471a9a3553a26321ff560c6f65f5069885945486b87607a77db25d05ae14a85975b98a1722d6cdf4b55c5188838f3dc1dc0a44a44dbc691696c24e58a8b882cf5e6df5b4fb9b3aebec020b49fcc703cb81a9e8c6077871a0047e3788e432dc097a0f94f5d0b93d79e3b4ce3337a3d9dde82217bc8eba98d7bc0a4f83a51d5399f0e2d7372abfd808534d3522d87b9c3472689a6321c9fb5a7eb4da44ef9a937b1b75cd4ba9b8355d5a753f832f5753ad354df7643fbb18e99f4982720e37caf84601a7cc55db1261e1ec1fe2e5f67c7b385d0eb38e6d29ae2753e50192c297dc15f9aaaeaf047ac6ec58550b3cabb23c6363da6120d2f0a04dea2946a00cd4be7c761d321701a832e2ba78c09032d7a0558320a2ce4a5e64ea1f32f7ed748e4dfc53785e07603b85ba6e2ee586422c10030f1d282ad6fa4c8a8310190e8e49351e2620ee92e5ad92ad69fe3146c6823bb265d106f4f75234d774fead4568a4f87a43bc92883e57d3e727fec1a74d35794b0fbf3e91038c131606cee4276400659327b1bf25b272d6bb64ec7330c28bc66d25a47389253ca82fef7535a39ad83b89035451d2fa4a06592aa3f3e969b8240e7c610348226d970216dcd49922696ef0e43f61c9c82dc9313d8be97a39f5c0fe2142ea67d73bbfd2fb04d85eb491e1322fa9f44a9095270c012fd903a4333338899169c26f53101cebcfe0f421872281e4dbd697ab8a9688a14fa5c83439d623e9d851de19af9569cf5eb9bcc10e19d00f70de52f5650592b0d95db319bc42823dab6946fdf3967f6b47fdd2ca2d768a1756b0b147b5eb8c48df1020ba3ad42dc72d20d08e4b70dbd616b8b671d96d817077d208bce85a9a676739c9a9f22aca4de1edb1b0c464ebb26c0408830b4d0328f30a18c911d84490e5d8b387f633032e963ee34a1ab2e6e520e7238fe50b79fa521ce7ee16daa8e6632055dd0c9f63acbebdb249243e5f7fa6318633d7594545f1a54c25afd1a2469adade0d8d75fd7f0f05ddd3f371c538ee71d7a072dd0fdd6a6a253458ebb432eab427c512bd4672447862049321662c40746e9f6448fc26877c92249eba00d3d6c50a2b05b8586c1a29858e426149337e5a7ddc7e75afd939ba2579d3c04fb660fcee143ab57b116d44d0b7c0e5015ddd9494fa2d029c167397cd5374ec680a25a27c007c7f242f7bbfb5371034b8ee825e2b38bfe98c6a4a7b7136196ebe4fdce7c18fd2a24fd8c4526da1788de81bef63b430c07c3a82962f8f247f5ebbbc1d8393caf0075dabd100c00df4d0e7099fb2f2730d524de3d0dd61c86958e7b0726a3ae3399a7a1d43507fc09504f4e6924ca6a2cb45af549db91094e5398ee4970cd2606bdb049c6ffc6c4e9d77bd78703265ef1739844181f9297a9f06c347377b770930613f5b39df0aabb525ec7023e58d6944d14dad1ab840eea9ba81ceab1cd962e2922d08a0c2997e578f83407a425efd4cd75ede3f85a3cbf5ff83967078f5ae9ef312f8d989689af1d6de1221a496da8bd69d12dd9c4e5ab13fdde75cf581dc14f4b8f1d4ee9053e45974ad1c44fda7bb3826e3a1a2e3779c2b6bf9c2ea784f2d0e2229db58e0f3fb06c8d0b3fcfef88d8752ae289120b5c10ace7f4740907f7cac96a4f4e51624aff450e4078f27b067035bad7b8c202567e042e82a6ae52eabe406ea6d409240917619443a17849a904cd375c12ac5677ac965f4fb753e1eb5d8244bdd028d1232dba29778f10a914b72151e52e2d3d9c9cd44813c93b24d396c205c3fc8b274c12bac06ad2b4277395a714519411272b1ad9f7f8968dded6c4e450ae89c3294c1ca04b5c5fb52492c5fad72360f928b3538e6d240d0669c94059fedeb3f19b8dc33be01140fc4b590017d668916a55302ecf1924b2602bedb149098c392bd599df6c8b99e6547c98aa74dc6ab140a0fc2fdf0c9943eea3ba5e99a8e44d05fb7014b631400e530ff117d780009299765bea2868931516cb907dbaf78153c0c641029ddd1062ed64ccae7bf04f1d74810726dc4d8e1183295bbf673ca825a101849d848a8b9ddea2c35b077360f9352e594816181258cfd45519c402013c3868000577158aa23af4d7d4184543ea37e0f99a7d45afd6d9bab4f64f92c03fa9b09db5f47bf35772717b9b6806356ffc2e94465be26dfaeabbbd6a1efb4f9aeccd7631b084d87b4e516ec0511506f57b3dd816a4c18674302a851274c9fb5ceb450b3d7d6cfe7a11a7da20b429e40a4d9d353436a994f290e466b9db9a46d38bb3b40bd3fdba0ebd3ef8dd5b8680cb9791d91d95a424973f51225a04502c28502c8c37098af2cb20ca8801e46646b0b17521abeddd02a1643da140160a18777e4222aa14a836d420b980f6f8d598c2c74ff8fd0927b6a5b50e08d81341cb25711cfca44b3410d0bf791db6f3ed01341ef8984fa2d777f681f0ba22ac68a932d565b14bdb3e0f86ba1b6cf08ed13a389e55139ba7c45128ce6953598cd5770fdcc8729003944c4d59008899cd15bdbac9f85493d7fe1cf1d6f9811bc4ee45660523d5de65630ef72f48653577e912ad4835d2eb18257f55da18efd8848d17bb7d10e28626fb6f7db15a6b0c0e146c7ef284529b465ff1160059600ce47cd43ad425156984e6f28200969247c60200eafb1521212c2a0d0004ac3f4c35619c042a85ab972122353a2c910313beaf52c0900f6b8da83f6e483384cd8365338742cc42f0c41af8648db0a77154c680c407686e9c66c68a62dc8650b2787063172612b01efbacf9ae4e369e8b5e9460a8921f3d1119282e3dd512d781349ed49543612086d2a03b88a4f19fa3d4fe1828468fd8f79d2e0b5aad349700e744143c2d74fb13def8e4bf9940c0260b1db41442ceae11f1cc3debda412a877a40c5688b9a4630d8f9500f2445247331a894a818027d00ebbe4f583d690affcaba289960c0f8dd91104265b6be9fb5fb54f7b503cd9bd29ac5a3cf51a030cb847ebe90be085759c5a8bc3a37070331c4c87a611f67d6b56a4e382b1be759afdad76d86f2528a4699db70d5f425217073492a42315ea252be7a6587f87747e8f801e8e527f84ce927d3eec7cea200d06001429f7e5e5352da77bcf818e2e0b91120472ca866cdb18117fde41674aa6f2d5c6104ead2d7eed6521e074d21d6f2734f049dea5cb3c515881b2fe9c12912e2ffeffb2576d7d5de48ab383f5e2b783695ecd059e800553d57e0b0a67f9d0de3177610a924c1101b3af30a5aee1fd90c128bcbeb1bafd15d7fc8f4874111aa1d5c103ba852a715bf161d439e771920e762b7227e87d47e528f91c4b4c9cd0721e957d11ea54e5d9e37490f13a8c337e3a836092e7c284997fcaa9eacefde623b54c13ebe71333812429ad42e4a20f790971bc9a335614963d626be23202f4baa15f0857c2713d1b3e6df4e27f04c3883d8bbe6546b5e7bfa20b41d6cceb934b40836bf331c55c99252677866a0ff357f60e40c148e78b93758b83386f21c03ea3c4b1a790869a2297e2dfb23a2248817ec8778edc0acc7fc91a46e996da080cd636105a2e4e3308fc7afef8f88e55084eb36b841f091b143518a83e78cbcbfa395decd5fcb677de03971805680dcecc2f0be03dd1770893fb3ba4a8f2b0f91c2010591109c07ca926f183c4443ac9007504dcb9d2dab18101d2511976ab1a8e407aa073cdd7210d805c712c4bf8ae4631f2b37defdea916f1f9d471fd1fbf13f36aa681bb00e250a43e77929bc01daac95514fc969649569803dd48259a7eaa0abe4a023e2226de551f4707bdbeb3cf7b70187e5d378f86a6bf8e4d5e08751b577bc88dcb6940a81eaef8c111e017c75162a1fd8320923a4eca0263f9cfb386894db31cd2d245987555f72509810eeb0ea486596c13d4452ffe7d36567ddc802323485dbe0e7c8c2eaef43bc064a88630793ed77860d0e2681557f54538038696c8c036884d7834a660b0b1ebe1ccf67dcaf79347c83d00e82fa7ad9d2d5f4c0ee0a08fceac09be596497d8f0817dfcd863cf747ecc3b9eb883707638d9f45910dd829173a570d041c54a40eeca0ec19b70c9ba34c4490b866db6bba204e8380facdfe3bf92617a1b9a01be49d0e241b68b60219009e7667ef3f95eeba6bf01ed7382efb0e92fc398ef4b7a134db6c8af3ad05e6300112d9978c71ec88cb947d430e7788b3dcf0e57b59f776b871e6aec37e0321ef06873328c11541b136f8c82f1455312e11e3f99417e82016a4a527c073a621f1b8c7ee7b3e5d284f608c127dea1f3f7309552533a316732637f55c243a7531ddc4a822ee42de17d35658e834bc40ee06dde77a5b39e346fdc1a375275c13cf1a7c4bc4419a10c4b305b36fd23fc94279d32909806d7355ac77e62f21513ce631755d9a8159a8e834532815ff30b699bd1e2470842cdb2fa251c686803e8e8ee83722a89a90e9d9e9552130b72113fa18ae9816db51d40eef668612aeaf7f0e516093a7a08b4c4c5d78878f63b1b2db5658df4d963dc6c742b3eaf836aa8148b91c886dd85259c8df6acba830aa4c7081787270d7ce53a2f7e9741ab0007112d6a222f81d9db085fed307b712d65b762b8382fb128fb7af5dffa2deeb9efd6da7adc910556f47ebb86df6cb5f529b6168e42429d1bf26c44907aeb045937a25e17c107ab06bcd2751a24785baa8a93f45b2880ab95c4172ab0883c07d6488ab684ce269c0ff90f69a761c96e7ec20144bce7794123efbd5356319cdfa3c0bb8a64e28d40c3953845f16a2cc11431e11d39f10e2e30b70239e686ab0aca1ea120c0ae455d44dd01f261a5d20c5c42d4096651f48273b68cd00f03d6d83664d529708030d23185cf85646a84864d3cc53e5c9361edd2c93bea3f9ec320b046f3d62329c2dce3f241891a91b8bc5c400ce591754e505bccf62f0aa78445bad5ae8065eeb4cd7101dd20326aa8a0a5c3fe2ee02a70844391501d60570e558a4564390e434d26377041f93fac0da2aca4d8a1bed6090d4d9e2f2064f028df988602160a0200857b3ee66482bf50a78f70271a87c82838b4a0e21e4e1f0502ef2ea29a8651db0619ee2fed68288d1e7bc6bbb3b669c3d486c501156fdf8e1c29065991c04801ffbca210a9c91e8433a2b2cd0c38a8f8fffc4314841f9ec25d4133f28fa79df795aa565c73a5085d0252ed06da7ca8786de4af812aba9779879c116c5ac71ec193a7c9765e8b1ee8d76e64c7da8e95d52d9f746d01e8b41c991a9f383be6e927fa5a45fb86f0190b6d7971bd207c06943fedbd183073829dcaff61f07ba3388cc93aa491898880823b8bc02496cd8a83a916df3303975f54f8da977ffe9697f597732ebb3da017977b1e98bcd5c903bbd5f3e085efd6db10d6865891fddee0e87981db8eb6a7b7d4b3390fd17f3a83178f73187da17831bf6fd3fdbcb9ea128cdf30b46c2a0b10c2d83740920a7cf520f0303d0f59bfea2c9169054de07e6b50f77081453bfa2be80fb07a235011f9bc53d45d6fe219a66d4b65a1cf8b92a0440a7e25d22486348174df86db991f3705f8800973c0512e8da370625911326f8f881e6d179408374b41e3e46d719cf16445609a99f0deaa2039ebe6c24279f68a1e84c8efc18e5623104089c16c9fb808da6c7c23b880000a80a4572e0de1103d0adeb73cb93777666fdb3c5a1851be7e9148a66bc53df109b9ee5f5e900a7a97c8cbef5d59cebbb0b02d6557fa51139f12be8fc33ecd6463332c8a6e00b0c151ad432a89f8a0b609781da6c767f678764053d2277fa78fe0d4ce5eb231d90a5014b1aeb3b2865b48e6daf44887f58f19b60154dadc7730cbaa0ada209eeafacbd124714bdf30a14909e38b7dc8fbc1e30bb09aba0ba3833e0a07950c77f821f0073ea343d9dab5d6c38b3d8cec218e288d9e9dea41b85f311a91b36088e55f3f8b72152dc4f3650df7023d36b9c7e571799ef23e4360d0ac0658c4e6fbdc8a7e32b3188618d95927e336b3d2395730aafad67c4abb22d3b48b37a55496b5700d6a0bec7a2ae339028083480190309649a0beb659d3bc64c5c1e601c472e0c0186768d4a92b490e02cd5bea74251206a8e3f27f176fd4d7cd9065b8b63c2e4e2f927b79d09fdfdfced2289a6333420909e9c700935c178fe6c25807584185dfd090316af7e0fbcee4ede094efb72b7c4cd19c13facf99abcc35be9f8b672357dcf6eebc5e9da3aad7e9badd0a2cff9de4a6ee1acb679aee4de6c62bae395f10044cb3560111b7fbb303b5ac4cd8e090fa6066d0772f9efed41f01fd0dda9d3e42210c3e47ac60f1c8fe8f7180135b3b76fe27ac3dddedb1686b701b7f7a8b879dbbdde2feccf6bfc18293bb94336ca91baf82abc5e51c88e4c91393c589024950d4a17620cc79579bd105616d4defd704fb13519cbb639ea3a83c778a56f1d1b741e042587e3433d1dfbd31b7be3c5aee1a9f3581e92573211658115b0716b1258123e94ad4482bbd67c84d5a00c078fbb700a0a1cea7260516f44543fe5e7ea1be9ae1de47dfc1fd3825958995737b2b80a2105f291582f9cac2850ed1d6ddffc84aba3d6a1ceb5f39a351c6fca95f93cab6f065474dad67ed08109e9fdc5a541e328c33be01f67aaf57dae67585f97477a5fed5bc168b4ad55939f751293ed50c2a2579b40c1bdef4c71fe34a3a5af36d3f752926358566a30fe95999313bb0e4c13e9c223ba79f359d5e877581cbbdc1799412e18f84a00cd34f6f7d3ac0af789564317fe0c277d3c0bccf28569be0410a867910ec4fb8655f5d27df685312377aa3df19614b2684a61427530d1734168aad084e758114e5e5484f8009cbb6d5fb9f3f325582983a7f8ba1856e99b42fad1b1fed477a561b57250dc4b9a000373e1b1d29e664eb2fbe7d223d217de93b43cca7c69384ca26938f9aec677b4cb3505b58bb8390db9c5ec39e6614ddf16a44318b41933893a69615b8d7a7f8a1fb0985f246b5173ceb01eb56e63d7518a8c85cc2a4b8a4dadd4616ed204299058d32b1b0dc58e11b6f98d205b2b015a2da85f9748bc9af3d188028e092998a3bedb0e76b46bdeb2f207479caa8158d580b2f27fee987961f9380bd2bf62d425df1f1136c43b0898259523550218705d317b71e7a91114acc7ca79ec163c26d100a4abb5929f5fce74c3783927345efb0c24086c5f1a2b66983e98acdf21922b49b8b210cd2640951ed062fe2f77c67a27495ae0b2b5a537d1314dfb8023b434de6a02aa347fdce99384b4799556a0a8f527cea792a58ad2a115e30cc96c3b68d8afd956e597b9a50932ad3924d62de15a033b76219470bee0c987b70a7ea7a36f67601b3500f6794a53917fe1af07411c159be21134f7e01da1da7875bb1b7d5b466e9e765d1d1974fbf0a56fdbcf0fec289ade4947787b108aa1cddb36211b2048e57f9793a204b7af0bda288e8787961e5823fba8950a69ab7625d7b426484235ba011e6837b64ab0ab5582a0175ca88dc920133c4b4cf427de41b2dc92fbdb0b83ef2c3c2d85e88816b9267f4e88a336be617dc81a0daf634b7a70819922a52f8ac9d54871e9403733b8a8561edb1b90b9384e34d9567061f41aa0ab3ad732722e48edda8ad4b01ae241d66a5e203580a6c913237cec8774b70252db62df77cea6c5be8e9a80cbad55b05a136306ee65683e32303b1b68dac39cc3a2aa86955151a4436a774b33d4536918e3575ca0485b786f3b05b267c97992e7d17ad507a244543de9a9105927ef6ad284f51eddf3f15365947f4e24c528e9e287623f78c07b1ad607d4e90b222c415a338ef52d0628757d616407266a7c9d375634ac389500b3013e194e0f26fe1bea3186ffe06104518b25ff42c78c19790666373f72d1305fda01a859adf2814b3691f2c8ad8adc2f85e1a1a21ce2d6033904de5ba5ebc35ad8b3866afda4eb2b4ea340130855a26f7a85643cc880eceaa68605b6a3dbfb152a107305cd6680c888362f4918dce34fb6ab3a942298ab1267fb0337478f9f7f7030faa7d069047665bf46b41a42b88932621d297c43fd4ca80ab7df07ff83c3cd032dfc7ecde33c30f44027384e57c648ff55e75d7a70659e4ee6151a0c6422456664290b6542181ab8958f96db7210a80e0f50ac98c20fd4af087b2545e19e1f854033c642d0124d0b496cc8c5160de1376d94c33622f4a15bf5c7dab88fc4ecf7d3d178273fc4816e2adab5584455eb71e8aa45809d0e2d2117b632b0db476c82e169103d134f9b1ed9539b880a885c765e88c7ac28077987a9aff91aa3ada7d862698aec7b079152bab456ac0a98bd2ea6ed14e2e729b635c5414837139a8b2e9a3e56b233fb38606bfc6f5f3269ddbf4a2288fd0d900af380a9a8dcd8044c8c23eb095576b07b7e2fe6ad4f0b54063aab56766edfbb81900e39c8cd3eb4373e8c1ef21d808b10097ad12aa5a7190a25d457c26d3bb7f4f468da77391289758b95f9a71955b3c815622356feff24fcff2a07426474c95fce1d6e340d094fb878382795f57f6821bb53c1ebdd53ae7f75131c2fcefd4de93f1d62bb78b2bdd2d4b8aa33935dfcf6524b4c8cf58f76d572daa2dfa65fd796636e27caae2772f08d4ccfb94e247ea6f978ca7fb72f938240a0dd9c4b01722b3b694c03c739bd0f35f07d0537a61462bc08e70eceef167c7c30d3fc555c3afc25da071a08151b7e97b633ffb6f78565d81bc35b9862164c525bb6c3e5dd50b3f74c8d0b70b2ac7c590e63d13f7f2a4840c4a3da22251e70022c8aaf808a21c56211c95ff6c190f4fbf20ba8a158446641fba07224331ef5da9ceac3b360f56b754543fcf6718aaeed60a55b24c07d3ed47b4911ccc811f5afce07f7ed2ced77a143ca39f8400883b29bd5af4d2ea28c174b8db8d2036ab2c4885652a7e29f880322b5b33b280f78892d1bd7d932600a17029218d8728b58247b87b3d8d65a309333ab4a04fc5a72d985469c9623fd9646bf0c0aab9b567c1ed18bb02925f415ca9178a3c76460512e45cfdd39a76ce6181975c7b484a8504a59d12d2664ee9487bff298796b8fe56efc608ead9efb4aa7114008a8c1edc7c61e86517ddcbb929767acf5fee0f88c3be0b69d0d8f77a00d2c5dcc4305fa6272c959ba99a301d4a104cc66a3410e3966e23313063f794d441a1b61ad3bd1c35bfe51b19e0eeca898ca4c6b73cf8484ded3fc59bd1f1a11906439a36caf9e7ba1ee66cf9770dfad766c8b6eea1d4f66444c11e4c6321b91e3d4a5899889dd317618ffbdb9e18d07007bd4a8a51571f78b628dc09f3a0e5ef2c3fb4964b731ddc7ec9413c5dada270917a92a45a1b711f2d21b41d2d73379c4e72ac9e8544d0fac781fbe0c978f2778abac3db79988d1efcd29e54a461d438557d8f7a349d976599419b65099815b33f6642286aac10011554d86cca2332f08f8bd54eb251e0ddda4e039a5edb804ad36f0ada6154dcefe1247013eaec396faaa8a0b62b8a055c27b5584978959e15c4ed34f224518a67687144fc4b176555b214d562873b6e8c86438e50df640d2617ad5169afadf8755516cf2485a4257ee28689f014c12bf49001fdcfc0a302ff4e2ac6477f85a775e0e5a20f01141327e8180ad4a5570a932320d88746101d37b5a7b45fd513e71873075168820f7a25bb0e24257386fc0e4c546817441e3671dc02a37bebf2778bb00c9e4b4426ec0a813dd910d33822c4fc80543df63802b73e7f06b90f97551a0044640d4f7b080ac12ad41ec3c97859f6f767f0d0971860868534606be547838da658138eb01a4a08c8cb163d3e14238530e47df1cbc92fb62e42979f2ee60d38c155d39720f4e2bcf50e1a848510e41e8642cc4f5e853e0bd358b3c5988df7684e8a67412c2dc7ee82c8be25ee44d27942bba3c19ff6004c45104ec0bcfa24acfa4aa2dfaa9173c2de8f8dd8997668eb3b8aeee57ab4de5a6da39bea0316bd0717880dcd51a1ae562e9421f4ca1602667a2635ce69528a2f100b47beb52d346edeba0cd5e9fd9e77c100ab84188dcc604763ef1033c20fdae0baaab032058100be715bc5e9e78b70f2eedbc43c05a305ad024df8527892fe67ae4c564776225818f243527825551291aca23a4742a5017c8a911ead0b8abdb55ba29cc724134448314c8151a684c6d40e39e7091825c2b2da98e2c9bf2db77c1e991a7860c779f94454376803f5901c0c799d6528e1043ae89673029ac30f92d970e5d5d2ac16dfeae2b629646bd85cd6f49f4747549096e5dc6a9a22792d6ff5afcb38296cf9fed4743664fd88555e79db63ef4ef08beef2f11c93c77295a70c9450d67b77d276a3f0a6f730b9744c96f6b628f91c6a284ad14806fc9356342e24e68fcdaabf5620a858f255632a1cec78bcd852e09210310e0b390810e94398949b3f1984ad7cfce25d46371f18b726bf46da6d0d34d87ebaf63f68e3dd6340e39823c8ad8ec7aa183b1e9f6015fead1a5f2947381fc638b46fcc4625c66e6f4c74ccf7f9623b06cebf205f0251727a8706c1c3878088273590448d7b13a4f327f98b270597f1241ae19010fae1c2c843e269855159ee8b85f6c0e68b1c49fc44b87c10c4616bf772093edfefdb17e663036065896a4f0019b68ab40af597d2c11bd667a3e92416d5629cee329bab425b035b5d2cd5e1aa9bea799fe84b10bcc2cf8bd4dc70a3cd14440d378908cb355a15de40da90ddcfe7549e1a43cf5229634851923b58c0375ba634a208ef88735b79bbf6dce3a37e163513f3ec24e03c7af9f72322aae888a564d8700f52c929f844ca05b4b03fe03f9ad7e693a15e75f7d73bb7e1ecf54880af3e30b19b5b43d4af0f97ede6936d175eff226f1653afec76479c50e1d22725e1fff441f6a871f6b8d65351f2ad0658b41c647a5596b8ef8502d2c4bd23de114827c6d7120f21b1dc796a626ac7595d13b967f951221136ae0926a96a777929e39d9c00a629c46c369cc61a25b47d18e6f12ad2350752487f894dd5e4c5ea2c99a6d097c7f247a44c3f942e70502ad0619b8cdd2cd391686e090b35be8b5026c54389337f3d700141d05ba4a121514244614e2e18b305e93e46008776f9d9d27f3bc7f1cbd6918e7946ff3ec917f4f851b96baa82b486aae676aaa33fe280011e1a2f6879c16dc284d804b8db85e0c622f064b199a6f7cdc6cdad72059047d94382b2be23d61a0fd9822717fd99818439f9610b471e893019c9732383dcee94856d6ba9dabc01fde077d38db87167c72955e88cbff64999b1455a8d4d20672a15533d0de1108caa4ab90008122333f33cde7cfb2551618a06a94584a35e8ef328cf53be8f3a36f9cee8f86277b2356e590a3e1ff559c72acaa652190dddefd3e5ccabc7eeb5cd370d0dd0172db577dedcd4f970c63ff1ce44156ecd2bcb24b583e8d4ff05d1d4e20169314bd63b244c6dd9b6467444aec3aec387940f4c42d8e4f1a3c26bb8797f8f71d99cc317346e1cc7d2841a5924cc3144cc0ab8655b2ca599071368e971e2fccfb377a56b720e6ba71dd7c710788194c0f5caa1a18a0140c0fabca9a955390af9f181b78248d12827f434149ed4509f78c1f594d6cd19c1c692edebdb7e103da65ba1b82ef27877f84400f2fabd19e48c2c01adaf23e81ac7b0bb164d566688b6f9f100cbd0d42713e24acd1389b5184df58df99e7aa3f650cc032090e801c0df0b1a22c0e4813d5410364c3884d8fb2798f4fb0a87e3c38d83df99ce037213146d70554742fec9f6fb5fa48cc5a5ace0c67b65f5d0933b032c8839e7bad5a87559b32882b290c71cab3ce6fad0a34d6f8ceba2f291e85b216da6ba59de05800ae2e34625f7c5e77bd6180df9e4aa633c3fba9463858fe0b2d7d7ac7f14ee3c16d1daf16b301b3c614ef35a0ddbc23891cf5815f52bf198f33d144fe5e7dc6f833e912acf5501c2a1ad3384e477311ca12afadea272021ad02717d21400b4b1d8339c4f7a965a28128e8debd1c2a15c5f3c1342c1e3e6a9c6aab959ce4cb531058805e456bb646b7f7f2d976dd8d67a5c6573519e9d1ff8dd047e572cceeff93dd016719faf4214d8693bccc52547200fac258c1259905df9ef1e1a18e02f7b1c86e0b7e797601f6921c07a2b9cba2229a38abc5c60f827e408a0384e08e14cd154280660a25351bf591e3ad58070af19e0ca9e4187a1f589a974ba8b8279d99f9437dd51479e949f27f8b50255fe74ba858ca89b29b94560646c96d9214fa8581b20667943d7dd7390c2c2e2825f8a541e98d014bffe8ae375310f5239204192e9a6b03636f5c9ba68eb25833103ec4b9cd35806bc782003f511bc0a0f764e5b89fbca8321c1fedb0d3319ca95fa28effa9f0daf624df0dd24927235ba45f834b691424250982f03836c909044ddd6003f562492dc70bbd28195cec797195800583177c736811aac6cb46bc5945149eb7603e3d8b7330155e964384e8f41ead5361177ca875caf1196edd8e757769387809086307222ad8b578c3091eed1ada6b6ea7cac7d71426aa595bc86fe3568f00fb27e75b7bec6fa7516e74b10ce899c5843f0384fb9c5a6f9d311ce3aa9e7331f8d6ad429cb0036af1ae975920d0b29a0e969f71b03b1dd57fb2271fd8606d4fbffd7eb98e7be68fd7f0d4bbeb8b1cdc7fa4bd7035cb20aacdfadbdd05a00510101875037cf71186fa093d8d57a0c915ebb8a4a2028dd461dd8484b4bdf7de726f2953923274095309190921e4b0841f3a24088429f7bb9a44f0872f39acbfc825891cd60f621efd43823f9ce7f43cdabb357a3edf833e320fc446c0ff70057bbef7b011ef8778620ef3136e39cc677ee4486137c9fd4cd97baf09bcae1eeebfffb091a1ec5d574fd7423e322f464f0ef9ce9c39ac49cb7ad3509321df89b226445064dee4b0fe2dece77012554b947b0a1379eb57188dbe086522edf2415fbec4a17c8a5d716689249136ac612953be648aa78d21c447da1a458324419be5f82ffaf9c8bcae7bc51dd27b1deec3adbb3b29a5c461b7f5b4cec6ddd027cbf9cd1c7ba7a761da57eebddbd32f4d037281d6e170f8ad160e3f7c817e69efe19e7e69314bcc3dadc3bdf640b8597d6c9847bfb4ef217cc8d86acc56c1d334b94d26ef035770fad5c986f50753f8ecef4d4ae9d43e0af6a03d91caa8540030cb9d1623e2463f1ad3c0d2c4882f52b821c6150a987206193f7cc9a28506b235f2fcef0896a3910eace4538e464a55f211314a735a3a2022f7072047231dc440a92887394a1ab00112341053818d464a44464a4339acad9bcf71f1d48fb9a76732ee6de7dd1f243a64f581aa8ce3aef7f576cfc3bdee0e714d20cab837128b6424a84f02ee1190e76cf631f74857c43c1c8e59eb0986846e29270c9d89746652df8b35898d464a41391a29197551d4d33359d5707863dcc1a47ee8bfc578c322796e9aa6694188749e3806cf4c48507f9523d8336733399bc96a4f2c92553c5b10739cd0cea35766cf3f27f5e985c1c794f1454d40b33f8f9e1fbd30744fdfd3fc7d70d9bd1b651efdba2b234c7bea612d953510644f39d8c3f5239e56605f884ddcc7a5ec23f391f557c7a1d4bebb3fdddbfbe37dbca71eec0bb1291391c528c2264ff0c954d6f90926980202727d4ffb3a4d47fa98b9bb4eb4aa695afdbaeb24869ed4dd35f0c4d51cda4b599b5a11e445df7132bfbf7a9d683f2f0cda4fec83e68eb947ca08e34690db07971b0890931774e420afec23ab3f6fdcfe053926370e657d244e5ed09189b808dacb13bc65f38627c89dc22a6fef51157ccc984dd336ba7dd3894f77a7f4e43886ce5b8c39a6b79c3b78b1757c36952f7b4ef92dfbe58ca11df431b3e657e67e29e5f7c0f101ce8dcba6a6453323c35aa9521675023fafe3eaa6d1e91d7374e313489feeeee2c5ef52a5cbd1a84b942e509a80391a75119a5d803a3160a817a9d91c8db8c892e57b9b47a5a6a0705c34d17234e202887837c085509677035c24c9f286361a7111c3254c96ddc64d105482b4241c8eb86009e2060e8c76814ca38908038a194f745081bd391a5969ea2af6831c8dac6c6923ece76864c5899b20d0e864cc615c57dfa6d5a8d3e9eeeeee9c779d97a2c26b9752c6368f3bdcf75b7f6c826742f4a69ac3fa5fdbb6fafa562d626be4a4945c47ffbff3e6e7d2c1684f06881f41b09100f1e4592a75c694563369075af4173270d12939242e4864af464d387356159716e4ea8417ffca9cbde993ce39290ee3c720f37c6d7ebc31db7e757d691f91bae4bc42b0c225070986057334e232c31839586a818aca0fa9276eb046e880ca410f4a4e3950010e201a42df962e966cd1a2862d546ed8a2040bb61081c396243d68714389961d28d1a283962a37c82c11676d33acc4400c7b7a238af5d06862bd252c2cd7450eb2c8608b7c996405071f7a304615305baae8a00aa01fcfd31e070e9f93ce49a3b6e51fc7e7b9bbbbedfc4a6e7ab59b36b556eb1f07d5a4b09a3b9713a76c6b515a4b93227addc79233fa7d0b4fa316376f8b1504ee75e4ceb6704c0031aeda21dafc1628e8e3e32b40849ad4735dfdd7b76a115b3f6dd3e8b7f011fcd3ec160f4fed500488c172fc1b776e22ac71c41d2ec523268058ac5f9a46fd5be8401f8ffafac60893618ccac833e7b41f4fa400bee91ce47a73d32fed26057ed35b82ac33649da5aac4ba01724889079a4d935358b0a2608fde424b92dc80ec358c2d58aa16569a5a1d34bf59ab5b29dc4075d0b664bb6831ca32bf59bfef9b737eb1308a74c4f8ad490d87d12ffd58c208d57ab55eb31b36be0f8fe2b8dfb8fb4deec3c97dc5cd55fcb546e7fbec824ef91187af1c27fdfa5556adad16fd8abf7eb1346cfb3585508d6a54c80b88c9fade2fab75dae797dbf6abadffe73fe79cdfda9535e690ffd99f21cd4ea4cd2de2297fc3537e776e1aa5726a21835c982eeca0397147f7f15df021455cce50a7300220c7f86118647ed4ad0e63126f5afd6a0b80efe113b87e4899f752e661a04f063ed094fd788fa4ca641481fbef4a59833d530662242840c9bc6f197702e9e11845e09e47735132f74637c8d148360a3a74c462dc73dc1177c41d45c9358abc79b3bea66d5dbe1563bbf6529bb4692b534aa747297d37a15fd371ed577c1f4dce22a9c586a4265946d2174957e890b4840b28a5946a493ff0b0451a466c61450cb620fae154851847b4123d258a76e28574a38bc591a35115a32b64589102a324c615273752b618ca09da62a64553f6daa31549dc4b41c2a0873139c87cf731dfbf23db759c2ee789b3c1caff6c2c66633117428b252d92b4386a2d9a3477646b95d1ddb6367777af2f8755ea5e277577f7da72587598bbbbb75e3107fdd0ab7558e4fc9402d93e69ac1a673f2e71ed9d6af3f5367ee1177eb9bf725172d68bd4ce8bd2f3d0d63864caf9498f1504c11ef341b087c796d46695582bb2adee9b128b70a08f9abdd3175bc7e3b6f69888b31e65318494c50d606419938529cb12166a88c142cb0b382c9a18aa5804c1c3868512251a16355c40b1801134b124e1e0588e821a4b135888b0e4fc15445a2cd9d31b8d03eba121db581263b92e98b05e1639c67a54663880820c1c14b1037bcad108072eb8a20952925115192065b9c29b73ba4edcd9ea9ceeeddefeda16bbdb6d8e8f43a35d9fd49fe255306eb8a59b1dc06aeb27adb5b5ce1bbc98e3d532eca669b50c5bf3cc77a452c3fc7bc694fd9b84b26bb7ce1e6a9ac22d597a529b9286e07280e2086989520b11e5d2701017fd98b38f03640dd7366ea4093c62d1d72fcb8ab07e0fc2f3ef085c99ad0893a35115600890a3511537c8281c75f6207ee7269df2cc8bc20e368c47df61b058abec2edde787eede1e638ceeee7e8a5a9037594258b4dde9bc9169c51ddaf4737ae4ac61bf7e75adf1b3748d6a747e194147094558eecb952b5c6a603d47a32b5a72b8928505b67334ba82e5cba1fd48b8020517e5ca143739ad1c8dae1091bf1c8daef49095d4169af042031dc23c21c552910f9329599011831b52a0822e38203f569f59183f185144173660024916a31801054c484ddca00ba0253880e36dc0fecc6106d9f0bb61f99cda4f0d13f1ac69d83de6fbb1de9578b1632fc11368bdfe54086bce4cfd69041d25b52f5276bfcb1a5bbee0c59c7053cafe4cba6ff29ef54fef744c0dd45d0aa66174690d2b5fce49e926e59c93d2eb714a4d6e736a944a31fdf2772999724899b23b5d72983f77a38c93c286524cf60fe952f6dab5c5b44e7fcc3a9b126d2a84d3fa91caafd73f6a41fca7cd6907eb1f337d9fb4b76df6d6997e77b75791924450edaef23f041b8dc07892fda5472944109e0b79ca1871a8fdf67eb5e8758bd5f6cf94387c6d5ea5bb94526e4edd5fb4e914f2caee4242b4d99c330b61e58969d07e5ea3614eeaf39dceaed5a3fda381b701eb25c7078329474ca4fb2500c9832f5d6eb05b8e465fb6b8b15faae4f085450f556c708425072553d880011e987e087a0349196d30a9e163b2834a8b980e77264cb2bb13da0019fb42e50b15b98dbed420a77234fa3204159e7ff418fda3e330767e2dd9acd6096fa2e37ee2b13c3aebe6ce6ab524fdd0dd3b9c9952b7eedddd9b776d75b70da914d343c7ac3869195ecc9973ce8f3aa5d35b7e41e967e547f955d64dbafc98b7293729f19c1e3d9cdb0a736a310a95193d479aebf6cdd3cbf9df77dcc1bdd7e00a7307f035b00738c139f1872b8d5273ccefae8c39e673b7638ef9f5ce9863fe365fa3f37d82d28fc0ac86f54b5665f580d5ddeeeedd4c54b8c005135a5cd154840d72c882088c17547071860a9ad0e202161a885822193344010321b458d2c4922c26d802c9166d6c1183031b5570e08a0b62d4b0811c4633e4f0c6136aa4c104165109893154fc10458621bc08528549182c7a186a42024b104684f961061beca082150440cc961b1c3da98249cc153e331022020828b0e43083a1964827a8c08a2c48c23802054aa4b2007242091c8478a2856c0ec88f557e0b079e77dd773decd5c321f71d0e9fe3362d461a31279d056f0120597bf079b8166dcb02af974fe1a546e65bdf7aef671e4cfd774f343df6a462b152f651405c16f7b86206924fac148b45c36ad17c7c7086a625e3cdccc8fc4ae657322b99d56ab59259ad56352b5c5353e3c5c6e6f57a392c2201799dd8f0191fb4c086b5f7a79b05365c84880f21e257de8b19f059bfa279db3a3df7a7132aa57a140e357005ee4f1670bdcddbbc0bdbe0cf7bd50d6b3ebeea74bdd37bf82b924faf791670bd0df7281cba3cdce342bd4d91cc7dcc4532eac31a8fbb21c7619497aab9a81b9e30115f7dcba656208db528140882a8ffdefbc07afacffb0a56d49f3c143733e3a08fce33cfdd19140a8542a1bc90fd9dbe4ff52f7446a970d859f5abffeee98643f249c6e6ca3163c6bc91faa2c34ee0ab4222beba2f6cf61b3c4166fbaa1b73ea0621e2d962f0a9042df0d97874588f0dae0e23e2b6c80bdb9831639a3291a66930cab3dab6904c2bab3657b72a840584c5f44a34dfcf78dffa15f836270f7555cf5d7b3a3d2813030e0d5ce1f42856e6388e857acba55ee686ac94973aa5b8d3a36e2824a35e0355e0dea63eac19954a6117047c72ea2debed9f42e09359580438720a8742b2fd50480e5929ef4f373c9db010564ee120deab7010f04f4258f9f496953d0e874258195c9d401cfbb5fad9af957d8a4339f3cdd86f6666263563bf99199b9ab11ccd0ca6a1a1b14ad6be7c6b5538f4c9aa67dd9095599fbaa1909c7afbb175646eecd7e9747a6b3ff68eeb674e385c7dea65de75635b1c7ecf7a1ecebaf15357fb15eabf57597b53efac6c6d65b15842580f46615bff8a3cd6b65affaf57ccb5afdd7742819fff955f962736149b4174b17179a8b78f42f9d7383fff1ae717b38f42c5ac6db5fe5f2f140a9542a950d6f2a05028d4871fbe50a898c5a190188a8727dbb7291b53a5ae90582ce6b96c6c6c6cbac478f2f7b558aab7b1ad77e1f06553636dabf5ff7addc462b1582cc613ab6954125f8072017234f292e475f55cd31e48a654a31ad534972f7a73ffe92bf536fa270d8740327d8a431fa21cc6f7dad10043b9c82cb9783932fa424c26408e465f1051cff366bee66b641e7c1a9bb73929b141eca33c7b55ffc9e7be18cadcb7828cbe9829356577ed8ba08fd59f62c091557f7a0d5c01f5d6fe77c357feb88ffbb8ef73a530047cb2cafee94f6fffe37e7543fbf12120f3297bb98fa0046496791ecd69a00aaa473d774320997b0d54e1fb93e5ec879f391704527f7a54087c720a8b0047b63804924f1f02c9a8477d685fe39e8228c82cf3a81ba25e854f4a2c0abfcc733888cca77090d673425e99fbd30d5158c82bcbe0aff5f16d6e3833cd3b9882f4d1c2b35f3518c4b15f343ffb458355f87028f38c0a33cff2666e7882ccfafef4dee97a665dd64b508510f5a716b275cf1254e1e47906874732ebe9535085ceace754f81e35245b1cd227e299f5e1ecd7e94fd827b3b0e7fd9ffe84c3d39fee49893d7d7789c8cc1199997b2ed69582cca89719f51f06bfb1cb87fcefbbec7d3788f7b5cba8af5ea0e4107234f292247b1254c1fb5aaf87ba27f9d5c80ba6d30d5ff9f4df0d817c332cd5d77c6b86a5c23438ac3338b4f9f43238b4a75fe1b0f5f9f429eca77781ffdd186446fd047d7446bd0453908ffa8a43ef517f4f1284800fea763e018543f95dfb0ea210b3c5e10b753b7f37fc7ecba747a11033f8e12b831f7e58c82b7bb87e7c5bb51b02c991cea690578e4166f932f9b486177342502977ad7e3d6f88a7ba2e8c4db9c36118444854caaaff8067f9dd87f503dc099f524e75af61613d87ddfb770d02f9ceccce8ad5d9ef704884668987b84cad2c6b57981a614310094c8a3bbcef07b980486096d6e9efbd064f881e77c1af3724421decbaef2eeabb2012bb7c742fbf7b896300fff4f59e9ebbe04bf001dcd750e6109c41ee479d70bcf1ea579796727f4a39665029d5540d051071d464e80c254b611ab085161d9a689a410e6cb0c2004b8a58a248081b828a905508f1a4cb0f456764b9a2240544506e300289222e505c81828a273d4042488d22caa800195090e0410b6600031e7c96d832068b3045f0f0c416565a510c1e648932051135a851839423786cc0c6ecca1931903246942ab42ad2478931669084e90a0d66c82225155efc901b8b23da00e20c1bc684b1e1c7e8208d323caa6df2bf7eb54b6f49191f0c2f4ec0c6294820916b729c824450e670b032cb484237ab68588999b87ae6bc46e81f99613ec5ae1fda11fa1a7642b1e599b83a4ccee8b0ee368a92846fa6e56e55f047759884923d8c4a327a75f9a0df14bbb6302c5175f79a439030f060f8667976d6aad866ac78c61fd661f147e7b36e8234c4e387d53ed34e081b0e7372589fe2f0068737b971c8025b37dfe784012ea1047141750ba250a0b8eeea6daeea5bd789ccd7dc28f326d8c37569dee6cabc37bf9a2b652d1ecf89884113226d0b1b4a28507af0b486701a99afb9d29360045d9a6c22e6f097573a210d209f9053a0408181e7077cfbf36b9e2f6f0c364fdc4bfae52f67453c4e4ad841b9878b8746e61d74699867b552bd668495485b667d4db24a332dfcf303fee97feca79648121a283b0baba7f99ed5d3ccac64b01196ea797e6840adb0eaa5030587c55a44d687eb0f4ae779ffff010d8783738341b835ae1c1c36333957e641b8acc771eddfbbe2fe711ebbb4fff17deb7f80ef7dcefd9732afbda75dcefd07e1e2609c7b8fdcbcf738ee11273d37effd910fbef5de77306ccf3d875d15f3fce37c087cb6fb02cf8fcdd7bc4b7b20dfb71e08f8de7b38d7b53dcefbc8feba36fcdbf6acc7f13e328fdb38aeb69892583a7225d6c861e4f244072598a4e4ea16a52861a575a413170b32ffdf23f38f031bf9bef5325f710feb71b0911becdab04bc32c7cdffa1ef0bd07df7bd69fde75536f73515f7355efb52ca5b95166dfbbab771ad78d52e6363e5b835100d5803740aa56e75abdcbbcbf4cf21d9a770f25925205e23b2c3c64e5492d4109462e0db51860f31ece05ffe6766f739dd47ceb573f774833620e7f7f18e48e3c43c2fc5da5ca5dee721899e810a5434d9e14597139253b52f6a4ec4f25513f692221dfe99ffe11ea1fa1fee99f89259364c2b9357f735befba52821b76fdfc276cdc51dfff85b82381181a1aaa5ff190d6f95290fd89f44ef7ee3d60706d98a7a6357fe659ff234303eb6998dc4d3dcdb53f73512f73bff74e37cabc283bfdea7a1ef8138832f07fa8a24ce64619eb46d9ea46992a8575fca0f0cf09ff7cf8e530ff1fafc33eb32ae3a3f587c3d70e07e79158d9bfabdff551e6abbff91fac77b1fe86855df2088e5f61978cb28dcecbbad661feab1b1db67531038a307f0f6fb81d0707c7ccf6ffc1c7e721ef3b7effde5f12f94eeb6d4078d924bb3cca9e050596755dfdacc62e166e7ef53f5ccfc247def54770fccd4799776f948170598fefeab7eb6201c7b3fec70734ffbd087e629033647fe3093b77f0900cf2e189cba1ecee45d97f36b54ed8b3cce42fc33897ca25e6704a24e6f007e1e23cbeff12ac7faf9445f0858a5d3c38fff3b7d8af9635c41cfedc9542fdf2c7b9d20609f37fc982ec5287ec154ba098c3bf9a6123100cda0bda6ff80131d3df1e07e73f8293e7e7a7e669fea7f5dfff7569ff1f653897f5388f8f7cf01f60d7affe080e8cf337dff301766defd2fe838fb2d5bb1e07bb368cd42fff1ed24abffc5d1a76fd603dceff58fd07d8889124b2d5e3e0234e7a3c8c6cf5ac8f32d6df3ceb6faecc05dfe6daafb9a76fddd47b3437cabe1b65a81b65aa199a8bfaefaa3e822cf068170f8bb5fa199b1b653357cae68d59067ccbc2ab98bda87bfa5a3e5ca392f871c2c947021493818fc4ca3e9b5f59a6f5f13ffe7c20be33f3f35df09dd5cf2fe23baa9fa9aff9302a45a693fc084a1f5060e58fff0f92da8fc4ca6c9ee6fe00c564368fc4ca6ebee67e3f73c147ddd6ab2ecd9feecca76ecd7bf2abb9323f7359df7d0fbe4bf36ea24c02117310c920a40ca41179fee77297cfd33b782803438cc85e6a9df95269bef43295f27c298f7c473ea9f2c9917c229f6c510ec51c616c7f3f58df2f13f0fb3201aa32f06f2e90957def5d1aa4cc7bd795329bfb832426fb1ec8cac08fe00bf42b76558f7bc2360b31c7fcbe2d4c40c2e67ff707c9cba2444018bbe4f95126208c5bf2e4308f19d6371bce09dbff117455fc3cdab337cab40944d94feb693cbf32b72eea69aeea67eee96b6eeabd2b65da8dd959e0d1f4ba78647e025126c37a4fd6dc286bdd28a399b911ecc1f34383cfecf429d4abecaf6458383a6c75adc3e6ab5237e6303fddf9e0954c2a9fadbd14f8745f3f46ee8df8d4609f87efb8a22b62170bdd7bffa33e878f705fb9afde1fa9df61d7f651d67d94d1d9d661f2bb2bdf7778d896517255c96df0645115199c88a66832c5d39486ae90117494c48a14652a0a82e80da5950f2989f1724212d34618688015a94ad0501540415468a822344b69454846985174040e4a565e44910f852f5450114d391cc1a888216c13312d0101322d4179723a2979e1461a46394473e807703626ca14aa1aa497ac4a535a298a982a8aa2b001258a1b5694b68da989bae4f0e505d560460e47b0ad334dc04118a819133ef88c092772f8f2949ea891c3119818620cce0b2ad2866ec8e10b350b8ae2464a0d474b61b27cebcd39fd6f7aa7a5349bcd648895d03a33cb478185d44549e4f0e511198531450e4708c38729d18a5f13a46882844ef8693fe6736d4ef7e9f3b7a7d87dfaf41b63a439462ce4956bbf2676c5299649ad318c9cb4b51268d97fcef623db949b656282a7a35f3d01d6a948a6e08d40b49fcf02a9d63625794cca1164414753ad7b4d93430e2be2b0fedad46129e4b0bea209a9b6c6022f02c0fe1018214982e42c771129447e18c10c69bdc35aaf7ebd8c74b50eab468ab40e93ed797411dfd9b0b5ad2621433429996cef6d17051e9907a45f3c5cea701e56d8f9a10e0ac6f7db867aebbdbda81b4f1e0eb7f91d27b7ac4d8de3b61b8ff2b7d5ae825f3f7e84716fb9e636bce5dac27b4dad0ce53aa7bff7dd0d5fb9fbaf6dcc8be489c3eeeb7bd53bc6c56a4c93dcd4388f656fd6ea3b435db7e18e0392b78f30ae8229e0c8f563eb70db2691a440712191ec46ba3a51606596dd62ec263f7337c87cd79e47efd8aef5bfdee9bebe047bd4db72d80e29f18bb1dba7c2c4d383a6949b9c5ac7973629adb1dde79c9476733d29d5346ddbe6ecb6fa859ddfd834ad664a7dcc2cb5f8ea62a8f32ba7c51cf437af5dc3542b41766ebb1ad5b80e0603d099a9093aaff3ea13a9b9630eea991ec9dee7776b19b7ee0379e03de34dabb535cb5a5ab79fdf39b68353a37ea791c9edcc8f72aee0ef754eddf19433d2182517db78e89e7376cf6eaa7577b77b116eb8e1467f454d4d6b30853827e8a3710c28c4dcaf6973f6a6759e73d2e9e34f84091346a39a0f49a994544a36d248238deea6dddded8d61a0733aaac7d8183ad3d9417831c618a30b9e8733c71867bc8d6395e143cdf99ff5e2ab75aacc2f7188ba9f17754e4aecf7a8e7c01ef2ab9ffc5037f64b36d771edc51d9d77026df75d99ed4966d0eb50cfede0bebf8b3bba07bf9fab608f0f8502a5cc5e67f95d0e2d526450ca5b8e5264d0d2f278686a312db5f03824a598c761f135396b7f4a29a534f60b051e0eab3bdaa4150645dbdc3ca683070a72167778aa8f31a9d56499c016c08fdd8a22e46e05d59511d67dea761f0e53b9fb70088254e63e822bd0dc7dec26cd79f5abff4b588003b28f1061259890438830fa3d42044ed527a5947eada1e1739bd35dfb70ce39ab46df351e4ee3fb47a711872ee448dfb0325e21ad1c3ffa9c4e7b73f941a247eb3159c396a043c7343223d0affe39828c4daa9d614378bda846d1b039ffdbbcd9aab3ac8d1cf7996042554235b781ad5758704af3603b273e2932cca03fd6675957686051394a9161480b6b739422435066394006a0578e9499971ce6cc2cd62625a902854a0e54102db1022a9aa4cc7a7841532a475490a824d9d698312db032841524c630e560a5e9caac613187d58e59caf931537836c459b5b0a91af4122352300606588c59c118962647a337c418bd11668a37c0c8614e0fde507a23e98d1bc248e28d1a72d88a15c51823ac8a905d01395e6fb56a9dd327953edbc318638c13f6fad618364a1a6b32d54e9d0c5e797696753653961fb91e93bbeb6e4ad847b9a598de69b5ecda42b9bd2069d96efc90fbd4dddded4619b9957277141954e1828a1dac6421f322055d6458830a1a437c210b2594222e3b5d14424c4714b965173bdd30f96dd4f075c97d6556440d4e90b1832264612c3a4112510ce184082b6668812c8c470aa0c111696039628a2c8690f50c4a4cfc2cf1c264064aaab07223e687ecee6fc550c9fe2d1eb2f7e08b170903c8dd6f608f2eb71b6b648e7b0decc1159570d37aa3e53b190c2e0d8b60c28021e6a761318791a461f23b8b1217379ce4faf529d8a35625036c613493857eefcda7c154240b1d7fc52d29a3226f596c5547a5a4cd29bdd3486dd4c60e794ab1610b69e7359bcd64609acd645d349bed7491cf9ac50899d222df69a4d8a4243454148f6a0e366ca4a5a80485a8099dc2f650ef48a4dc4c2a201d5ad01337a67052050921524c4e6a90848a2aaa80f9e7c2cba9d260439bd95022bb7f4d95a1255b608ff96dc106104d54647f1ab0874b218515323b6624166bac91fd65c01e5e7d78c8020754b4e00c256bf4200bad132f8042882e8c7801963164928b143da8fa95650f6cf86fb44610d9fd593e460831710327b6f4c04490cca708c1e409274bbeb04048e6518c1c08793a3e672501d059fa8a5640c7f00ca6dc54d068028d2e6192480305b93f96c613b9d5c821b71a5ee4fe14d8a3bfd6a952c8e8a48e9d6718657f54b52271904356ee58258721e466dae195fb2b1314b9cdf8c92d0694dc6640894667a040471565682921f783608fbe52061437b9ff8312852675bc6944861bd9bf037bb87d753f0a53477fe1257777773f2d21d4a1a3752219485891fd372e0b32900420f76b53be581a9178b044f69fd50a1e2cc1b96bf7a310811e202dbcbefe707e130c18d9afb0e10483c48edc1f6899607cc8d1084c921c7a5d64f921e468348692a794656bc9764db9b266fe5cb46b0ae18b3b5d1d2216eb506dd2c4ddc0863eb91bbb52700f0c65c72eef63ee3e66a11c76609ab2fc0e4cc7d43a615a67861f93f5cfadf5472c64cead36a9430ce9006f186b41dd91ef5420224c7e119506b6892cbf3bea8ebaa3eea80ec51c49de1786373d214fa8772493206f0889d681d23a535a271aeda069044fc823ca3227879e50965e5196de51969f2cf490b2f4a19f26404449a02829d2e1a875c25e0152eb84bd24a975c2b622cb0fa798a5d60929134cad136e5eea0ff987b8a35f26c9274fe45819218772888b17305bb458fbf6b9258ea90e1d65f95e5a675bda96b6a56d695bda96b6a51a248728133198fc3056146a4db27c1f8d884211d33a74466774566d2ba433dac48ab8237ac94cacc53c3cd828b3df82c3a41759949d3e8c4a3fec9fb08b07f5a78fb208f258ecea25d937d2e946bca475c216f25ac8776a1387c9b7ad3af448597ef8922d24bb49964d946543c9b28bb2ac430e937588f60ae20e77ce35848897cc54378d4e1de28e5aef07baa88b5c458c28893bb8ff80103005c9349be50ebbe4cc4bae377ac9b3244049801a2868894b0af9209f42f19d3a44eb10853294a57b4a39827002f7015b004bbc98e3f563ade3ea8f597edde87c22bbd7b1b3f0485002cb23821773420fcafd1b1bb68ba69ce2aafdeaffb630ec2b15257d8ca2002b426e0ccb9d1461ed95d139883bbf46eeff22776c8cdcefc5eb2ea73b520a51ff8790fb4d48ca5d248bc45823879f184b3316b54ee84972ebf47b92d671d97ef56b4aebc8a4dc5b14e5a44ef29d564dea981da88b58c3c8dd65e4d6e20eff07c4dc4af995d9c8569f7b0e1b41c1617e14996ccc21dbd583ababa7fef61557cc13afcb310b2dd370740f9addcde04519e3cbea9365174252c6f85176943127a2e1c5e875950cdb5fc9b09ee33bd8836e3dbc2f59fbbe9d1b3c2df922945dde77ef3df7ed5d7bfdb70f43c83e757cfdaa228c4745f293f0b8256c17638c5a34245382f27c9b3bfc75e6682406931cdbd8700f2bf5e99e91a817a00a4b20156692498658723474944252bdfd1fe0a7f0119c07dfe2234e7a701e04ffc8075136a506aa86867c471e491f1aeaa3a3a126371f0cb91ee7af1253cff02f3535502fe9201c371e8eebc4e6554f43cda7fe6f94ed90359036c81db9c41309f44402d5a3fae409be377fafebff4a190e4d7301c51d35c41d49e5e8a856ed35faf371ee07373aac56af5e2d530ee5fc609843c41ddcfb37117147d6008aec1f8279ca13f924e6e81c4a9c0772f338383a0c88ebe592c3ea87f228b5619e1b7cc4c9f6476c5ee5c2476a3ed536affa9e0df7a8521675023fafabf1d91a65a5a64665f3ca3c5f5313975c18e23b372fbd05249b3c219250a0204171dd28c3b9aabfb9a9afd7e66bbe7b7985549258545de416d79cd2dddd7d962448680ab54e289b829604492fd28b6c925e6493f422bd2021d9fccdad79096e35b50345fae55f734309f357a991fd8cec2ed4ba611e1b9b9a0f814ffe99f9ce0517e20e30fbab3829d67318975c2cdcbceb81dc6017766940a2c336ac7ad7ff50bd0b1f71d253f3a93f628355ff23f5366f838fd4e0d44799b7badeb7eee969eee7c98037ca2cea5932d6aa54a967b56e94adfea4c2f604de17cb67ebef8a177342a934fb1563cc214708a15f3344fd8a5fd430f99afc0dcb8e9445524a6192e94c02cd874512643440fcefe5c746689dee5d2778e69efbcef2735e7107f82101b2fcd337d8a3fb06bf6ff00133f90539e68443995d358cdae528450a19b9bf3b6ffd2c4b8b2d2a7a295278b9e997fcb086f6fb4fb2fa40d4422c9a99a0183f569342bdbf6cea9f9ef5cba36ceeb4de6120627b47f5e1c7ca2a5cc47351ec6261f5aaef59bdeae9abb011272036c27a1576694666341f5917a1d1a824adf7643dea102a1a11000020006315003020100c874442916830ce94611f14800d7890446e561acb63518ec330088220640c32c400000831c6186568688a0093418925070f51b495328a1c1f882074a16792d5993dd3ea50ad3234e7ec080073a8a53413624b5245cc1331e9b651e814068f0280537aa8a6f1bb197aee1c259ea16f13cd6f5733018c48cf38d6c99ba17273c94a47434f61e0e2258c153bd08b403f9b128d8b3dca57758f987bbf06753fbbbbce1299ae9149f500ef00d7d899ee17d8869f98997e29bc578ce9233fe1ea6339ba74687280c34ab3b5a137a97819257aadb3ae6daa32d6ed2edfa38c0f7eceefbd5d2e2e70aff028dff84389889f47b581fb1becf562c675c1a8511e714252f931d986e46e3e35487b27c867a17045be9f91d94d94233506c833e37387faadf8e253a452d3b18d9d8dcd3c93161872685219024ec75a53a41bacb266e20dc0a5ae110cf94224fc708d386de6b0951b743f8b9f415a3c7d99969f011b1d00a6cbb72015afeef9a4435f789f74078ecf479363c24080259f0f7680c371f97c5ea3cf876ef675e98279e1749fd8f863e71dab9ae319fe2718091d02d8109e46a51a347d7d78e6d651ff2f17f511dc7bbc80e9352966ad6921ec2f245d9fd141bd5d9f2eee6a80a861c0fce6993efbeed131fae02ce68758c6efb2ea1664122d26f14c37d78a87a7e9a85d6140fbfa03a0bfe9ced5d9c4dc184266625e9971fe9abd3e031adad35a402f26b273b7c4a3a05ddd32777b2d8e05560a2c3a46ef090bfa117541c1227a5713711708b34f9546cc7e17aea1fcfbaddd260b334d83e2f4286924abc274494423c7f06b01c5449b7464834918338974cd5a0f6886defab3c0833234a539be89b2318531f42e04f1ee60b0f32f1eaf960b5cd68432df8a1d900e69eeafb8f025289ddcc0389dd65697957fe3805aaebb10f4130d4b986ee0c3e8cda209370ae655a88290b1a4318b9ab161fb5c7d1d1db7454691de07ff1c0545ccf026f9f1d6efec8e46d290d3f056f23e8d61e0c1e30182aca122fba0e9f6ffebfa449e98ce071be5c2c3106e60f172c9870e3a132db8ee80ca23f6baa0771a09c54cea296344a0b2859e735a3578ae9a3992d2399a6265345ef424a9043735292ffbac90461b4de3044596a24c1088cfa79422d21bfdbfe45e8f3d6bf7a0c07ac23d788e47016b8d1fd6d1101b56689379193d9abb0f8c12023c5b6928cc9131a5510917d9ca77613493c16252c0a86e663f5aa8b0dea83e76dae358be9bbdc5d99f559dc7579226401893ce6239e6fc6ab541909199f57a4d5fa5552da127ce5e96625c284d6d5fa66e41b834a29134e04766ea02b0a558ec8615b78725e0982b6c5a20e626c88ae652740bc0954d45b7e4f01809a1b784bc3123b1c264a453078f34ce766a4848c6c35a49eb5bc0b46c83cad75918e33f31e17dc00246aa1324c609e2e2b8036e74ce91c72b601c096c0aa7bf19a42da89ecc9a692c4b08cd7f1d6ccb2cacde9bef8478d00b6536a0d0bf5234bc266d12ba4c61b3875f8476ea201d21a6369018bd2d53f2553db0658a0a6fdce74ba9b2fd9175dbf9937c01289982abb86ea2758f7ba73c812a3ceeeb683abf9ed1de4b284ac7345f7f55bf4aa10c9f0f312708ac03774ee92e2ce2c8ba69bed1ec3698da612c976282d5c8a25c6a90f74dc4833f5145b2166e2a8ed926ed70cebe753b1f67ebbbcd6c27f845eaa924e04d7d56507da8f7c7a0c5233254fa25dafc53b4fd9891ced3238b09524f9dd42681b4223de1e475cc99801b8bfc50aea75848b02a896bf3100e33dec67a2a2f94f24ae64e8507a990a0b0240b56031876623b9e61b206e03059030ee0dfe2555ccfef2e2090dd993c5ea32f98e01473972a8cbc616fb9cb4324dd7ffbdb883bbdecf9cd61ee1527c730d6ad7470c198b5ade198f99246cc123cc911c9f94780101d11f16406559c7defb7b3a592e7fd2931c9b02e72a83c2c3ab181bbd904fcb211a481fb76d6e7ad147d06137a510bf7a5828114185e6e78332c7f22c9b4b788a6176eadf64e1cbd8147beabba4a7ae67fb03cdd47cd4ae3fd31baddff42e65e2a1cfa69564e8d2c3c31309e157151688fd0a62daa61eebf94ff2f0849f8807181066657709671b0fa044ecf892d2fd5e66b82bef2278a707a2fa655a4c8632e890a2cc4215a442a000d4534b8b886adf0970dd0059f02f2e4a62b6dc37dfa7c6fb2b2c58c88f2bdbfb7cc82d3ef1b4e7c3d24bebd4bf567dffec3f602e810d6c1d867c95583ef998fb00c5449e706b4a886ff2f75d19101df5701769cc7a477487a61a5038d0e7509089219e1e125963d700d5f0c31a537bcb3f0942bf79020b1abcd4255b36d5147508172c60d8273d3f1c79ffc875abddd0077c7ed14f981839138982138a251f45e6a18c12315f2161475662653678497b34c50c175e5b391425186c372849940d5e5eb6cf234bb0e970f38592c1c4c971470cea5bdf4bb62973caaafa8f9167c58864fbb3bbffe47012f75eab62d85d2a204452cd13886fea74ad8249110f6067ba8a14e0b5f7353606caea7e6d07d29d24a3b81cfce87e71b1acd852d76d526e40381827be75e24896ca843540623981b2b95286f20ab7770a667d82bd9a8cd7b9f08a2412047c32a0fb791b2900d82207d9e28911e7398450cb75f4fd3938b05194d3d43c8ba553aafabb789a6d111cf4930bd1a7b9110607704feee7790dc0b529a92e9ed3a1c5f5088c9ef83d0c75d15c31db4558b61ab21cebc723c661394f495de125ea18a1a862f4824842bb5cf3e47763822f8ca8a4674f193dea52c76d59fb942193e80fc87cb7f1e12ae75ee0e83ab082dc06ecc91ac59f7e99f57f9a1ce651cec954b15837a48aa6be74c7b99a850a17f26140baf7e36c755aa93ca92249e49ed8cdc8e575b617a79728bc01f92a129cd4e24449503b5c5e4004a42219ad74d17bf98dd19cbefa33489327db742b861d4b015301577c28b924c049558f61fa3dbde663d8c5dc6cca67692ce15fea2966032ff3424e1872323c95c5e19adc51699e8eefd96606ef491d5ee5336c69118ca3b57832c146758422308960a2ae44f0e6ad843208bc08256d86f80c24ffbc1f182225b7078158d897e5081eed79b30e79c5ae438984d2839c14a8e4819788628baef5e864b26c462dffaf84aed3d8391a26c54078536b0f52e7f88e0a45acee51997f9b5e4f54a3bf94ac7ad8f7dda6b69ec09410806a6e6cf1385ddfa629a489232c03d28fff8feb8c0e89faba9f6063515e6df259f045bd11bb8818063c74714db55ce38584f577940d0b7d8e427acffe7f600eeced382050385c368f2de4667f27521633332ec474c4902abf7a475ec494bb509bf51ee268b6287cdcb3d64f3a88d75802aed6f8664f0e08166ce836a236afc04553cc2b8b138826a79f3a40d87ffe2fed4b23709120dd5ad69ddec01e6ba866007fb82fa338ee6d84f93a407e61ff9e1bfa083f021ae2c86c15d06c292bba8128aea305e7272e0c40b9eba008ede3a3d902460e4a62b9c05f66129c558b9cda449455e63b7e0a45c1ac439cb7fede35f5c1fb5b0b0fd0118c183aa8f439869918e0101410d4de4845d9dae3af8c802094a654d906c16e6dcafab6bd4fdd972264f94221352afb1ef6e4bad20f2e28d1dfd97ca5c50ce114cbb8d82bb2e318ef16207df532c2c8837d88f819f0590b0620ac8cc6a19f2ad0a58b9920a90d54a7bea3649b440722912689ded537ec4e7dff02d0009e0336d2b00617b6023acd11c638aae30fc33269d2268d1e3452cb3f7d88bce67e59ed7006df8e2bca87c98ddf374bbf01ad6567d4831f242c543192b4740989488542c8a790c4a9240a98261213fcfc143ad7009757e736adfddbeebd52cea676b9758b12b7fea4c77a400a7968e40e836987c2abf394eb062a36e567cd0c03afc62575c75022cff462eb8f43300d6f4e1df6442e84e3caa20fe4ce642215abc68e40108db1a979f07fe3ee4cc7b28c79a6c94dfd858eb194ed8fee40e9608daff2068050e09814bcbb8d7c005f6493c57b02a62711f73fa70150b94eb8ece7ac41ddd96a444e8e931507ed9d57709cfb204056c479e5aa3954ea25f7032d475a7c0fe0a9a190cfc52f517980011309f00ddb95e5fa7dd99caaf7f0c72b4a40ee91ab23c502f1d34ca3ca7dd022629a459e812c540fd7099a4fb1e63ea16bc3f6a10ee874727b70bc61afd027dc63b24ddc046a90bc6a470021ab11eec25585cbb8edf868aad140c66c9baaf5dfb1058f1f9a43998e4c2d4e856af29c968b4447f8b9401d37f4f0f572b854db323e3023bfaa4d70accb2f61968135d3733942387f0fa09e5e99c3b271d98a6ffb32ed557a1b444be3b1e9477aeadde3f2500f599802102dfcdc0b61eaa6eda877f852f06580e57a5873031d5fed08129692c864ee5ca08843bed2e6650d224b7c47ca8247931df550abfb3e914013c4d53e53ed613df8277aa43d952267c6f925a9c76b71545c3052a81fb0a9f42896ae20fb36612faa4c7221a7f777b1d7683e04cff71e1b9f74af987f53315a7a4486c2a5d8adad0a75143c9856039b988bace21ff9975f3aa8dc8eaeecf1531853b56594924fd77fa5fb91cc155e0bf481c157d1408bc7db73d283dfcdb1a787927f4e19da21fe95242879fc8680377376b62cf40b1c92ae104e46c0ff0a07c566abf3574c86f3334861a8facd191dbaabfd5ec1b33256a2a1adae21bfdfdfd31b6f53273d92b84a76a9f21450e0490dd825f2a8a5ceac074a0e88a5147d52edffb3641a18047e52e09e3a6aa781952f97518faea5d9ee47e98503ef99dfb397466e0efdd701076f4b2447a223636ed6c0909a99fece401b7cb6b57713090f62776cbc061946b25bcccb895f650150c05775da3a78774929273fcc9fc950619f9ec7b4e478c1ece289809b3a15de18fd86dd62bf3125bd729fc973cc4fd0f3b3cb2ed3b1368cd4c18f6aeda58e62320594a793ec02a4f4967c63524f22868de1ad157a1bf83df7bae3689fe679914ba0e5b2419190be41b1950519378b59540f653b9faeb840167605c71cfd7ae565c530b80ac5d4c13a1ce773e66b73866b8fe6ae5f02de11c12d87eac640bfdc87b95a8c365c7a0372c42860e66ef99e52d66375c4cc629379dbc7a1206886fe1ffa387ef79aaec45568a7ee7363d969380554f70e728528f96c59186db8568e7cf8976c5200e6d12b71150c1fb962a5a687c1af5dab7fdb9b2675ee44bdcf9f3ec547b76f3b2800574d97426fce4cbf3886368a972067607625adb8191a570c218f0fb953b2831da93776135971be5aec6d9d81bc429d8bac2bcf8f7684d931d7ec9d178105e964f59c3c19212ff89f39c485fd13dc59906f389148e1b59845ba387cd6b3159b58f8b931785ccb0aead0d87fb23667ca35b4fb8be9a18de949942830a5f808fb00d4800cdc978a2ec29631761cb97b9eb6934b185b8b64b3f6e8b541f6ddfe642e8dd0f6ddfc60b08a034590c5a60e68599e60775951447a2b4015004daf3156c1aabb076989280928b79e8e294065f03235de3f7a105895ca044892250e6fc0ba3ef26cec943c3528c2311dd7a2b7c35f19ac8e5c22f6f40afa869ec7026b3d82254caf4fc278fdb70e68c898ebffc97fe3a528c61f857157a1406ad9edfb527e60ca9cebd125ddffe00dedf0126b737d42212d024aafd6a8a853e9df0d37e439172da6dd88d648928dbfb6777aa6d7b9bf1d6e9946f883ca7faf024537248f086f2e67c1a89082909eab9b9be79745e9561fbe7f3d83f5c49b63aabaff9edfb0576755f0e921c2777af8397a740f2209803bef5d6247e48721d7fdb0abf8fedb91937f00865e5c4e6b8ef13fc6c58ffd58226e4a32e7e340e50eed8a9e37b5e5c190b04b290fccc573f0ebb6ca0b29ab77deee4df16dd3d2b2c02375d03ee848d3770ce7972bab013cae723222013534f743882432eaba35dd234a6472754bded3ab290b36117033bcaca799c550bffcf9cf3d3f7fb7f0354574863dcdd69fa7fb81645695d29b62b0a97be41cf04e81ff6a64a88dd7c43ea5b81ef204f1cba844ff47f9663d41318ebfb9e551d657ac1d2e3923101145d9e6883b3bc8e9fa5bcd6f5a4a9d6c46e2c022ccd7040cfd85245c65e1c0251666d9aac03b7c361df7b17ad106187a453736803e10b66d59c503ff98cb34f871a026a12a14fd8adfbdbca102f9b7433d74b0112d54f39d4e8c418b54006d735d1c5e9a54cc5a6c4636befbc65d11ddbebc2eab0777b472417f5b4289bb0d00067336aa9a4787b5ee36918ba82f17dda7c323df8dd554c4f8c29346ba54c56c864ff03de4edfb95d4075da6eaa132d7f739f5597e1d69592196b89a31f185f90aa5e36ec5c83cd9c33266819c215b5fc1f6b50b14b8ee1939e50708a942399eb45970f7ebbff3c4d913bbb213b826a0a602d042d69ec02a3d5675d5f57cfc392904b4e8c8a5e095a4f631b28492f1bb3eb9d3ab43c235e25eec5d33b2769cc57554ce8832f091dc8cdf33fbcc43e8acb8b1c269ad4471a5da9669649288d5c0e4f4c504381662a6baf8f62fb618197a23b0994532ea1a7742f252b3c293ea41e05843851badd652e8bf61ae87c0d25ee8cca8a499243f3bae4823a8ceb643b534da033386e934bda7fb3e0bb0773f66efbfd6f91dceed0a035a23cdba7a03786f7b497d238c0fe4c8d711c0466b76259c3926a0f5eecd09a863dcb749e8046b4b8f9b9fae91e7c9d838f6870f66a23745f7b2ee01807e28c0e2229a9697373a3f160f85557ee8c14a9ba0c206d649250b292c562289bd9f348e8686a01a108165b4a5e0ec76b6f792f983d6db73e4919646caaf17bad7c244510fde68046ebdece5b3a12a5173e4c41f3859507136d6f1f8670d8ecf107191bf7ed3060693beae3b084a8dae279821bb5a59b14b8828ec5cd35425090ad81c5c753ccc7fc7deeacaeb97dbdae39b70cca9153118452a82f44608e4da6978dab51b6e78ec4ef7b21ce3ee7663563db1753c8384a5726ffa38cc46d7f3fb07af6e88a87e949b2d3d9ecb1c94e0bf41c91b610c848b75b1b0fb83358428028cd057c25388f8568bffb4abbf5678cbbaf2971cd4dfd3f9e95a30878fb463434b6382866ad239b96b9069a885b6003984fdc290ddea0b717eaddaf13f1430c24ca8ccbb303029ae2ad21a0b03e280281633f0aa944311ba840950974774d70060d41f293d80e83d75465f3c8b4e68155d2e8eddc50ffdb63fba80f1b0cad3b72543549a5ae1138f730e6719702861402eb7f5444bfa607a037ac6f7944abe7ff1eafbcfb79e70478cc721316c1394caed05cbb3752159c1eb14a482997cb242761ae60fde2c6e702e644fc51909f7ca7e74ff6c8b46e765eef3b791d0307e51025ecf696818e8b53da93c8add02b7d8fb75470435ad4d04cc2e398a6a4cb6d12ddaf6e0ec34ec5a995b5cdc558de00c397f189506af509f8534610a31eacf93c5b502789d662cf83af9d252c98b8b763a147ab6c5abb5fb27239a35e17d57983769945c98bf37a09cb39f40190dcacf01d04326d9cc134f75c25e15b086221e9c0995da3b0172702a70764d2d921934569add598378c9ebca2b4ae99c64422c94faf143fc46c4f1bad154f5877a486ab9cf5461462980392e7a2854fc3278e359cf91940677bdc23ee37c83fd11cd3c643a33902eac819c31e61994acc32a310a0e1b1b89a6e0e02e80859c3bd6ec0f4a10ae99a68b2e2093292a7c1be42567dbd960990a0e950e1049f6e20e3dd4a8e6ab51c6dda01542525e733671f9de83193fde5b145eef258b8f2717934cc8755c9be8abfcd44be3cc4f1bf9ddddfab20c241c0763760b59b9f0f87d6d56670c7d2445bc5b7f68603aa9987d42c3aea98b823f2547510d71a35d43ca14f0abbac0004975241d2483e7c8747efc31741f9ae8e9e204f34881e6ea2d9643a9ab650fa83851513422333cfbb2033ea566a82181dc8fd2c48d5d5b6117f404b908f2c7e44ebfd2a4363538561026ff4c7546107de9c431bca649f3d2c09a33695fdb705312ab845da5bfecc9e310e9a81a6c5f5a62b2dcb3b8b749a236d1e297b46e21ba1c54ce7374820213e66d8b9c839eb241c830e62c869582df44e64c688a47efa2bf9a8c60d5af052c92c4ba37e0dc6f6589d5ee2a5e891aacc9782fcd3b3fbdb5e0af82ffd91edd7a4790ca7f9b23bcffdb82d67bbe0b014cd4cf6b9ac7ba581f2999a45db9bdf35becb22059f8abc90ad1d04bf1640437740507812b667868c4bd84b99a6d1987f288343e4172b63ef8a57ec68842e98ed47c67a04c0bd98a8cd252d628de987e49f6d89bb43697b2d06a04c733a909088001b124e77185a13caae11edd1ea94683b8d7e3ca8b176f103dba7cfabb629d53c481db799a0f6d726076c938e969a4fe47d1b1a7ad975cc7d4df382c9cf3e15c131729ec930e66876ce12d72ca1f87d2421049966f8d16775d5d19c43b45bfb03147b653ce88eae28e234fd69a785df0b299c1a0910b9ffd4c1da869ede0388fa4e1a5ac067e4288a6c953072d4fdde21759afd3ceb8ff4fe1fb533e97e2c433e1d735000d35a7d51c2691a41da15e57d3e25d3ee4dbfe3e6680469c5d2d747322d0d92a5bd742fa93910724292ddca978c5aab65a0aad7a834438748699613e87daa4b0fbbd157c1b4485d51d95c7be9d8b2d2fa0bdeca018771c1a39254d9566cd19978ea05009c5873846a79e4a4bf337480d569afd41e723fc979e17db4990667379a67c0dfc5a2d3d45ee1eedfa54da29cc7b8083be7cafa97e93e362eacc112cb4e89d7d071d94d1916aa27aa369eacbd398cd08ff176b47545b55c72c43b3c0dfde82cd4b2a16ffab5dec7ce6f4a8a394d1d8196f636ba483b3e7a02b19f3746b261e0d9352b04db696c1d41d0f7b211b24a73caddde8572a8cba34a6b7039b11003844086e2be15019ade47fc15361a3ba80473039aef4357aae6cb436a87eba37caddc55f385228178690720c1839a0531bde5eb3de2384de9d1cd575c6bf5d047467e6ec17f847f9168e4583a98ae22ae9d26bc94c5e1549eda35037e2202f4911bb30f81bb594d73ca7f83bbc7d823be054b767fe7acffa61bb10d24b839a29c06489579bece77549bed2730c62fb68d68f629dbfd8720b45e027d72bbc31ac529aefe5be72fb3b5aca067d497c568afd2602c5a3c073e770a67df324ff199c3ceb4a073e6f0ebdc866d730575d510946423144a0056b0594811151a8da026c16e8c64858e8cc39f32aa6bf2b112c8043311c709f74a747cb98caa5d91d02c38669f7addab4304aff3860089409d5de5ab39d638fbd82423b49a4dbb5d78535e99d36d0a951d2471fb5a53b0a20465629020e30a2a1e64d5b02f116347a0f144a344bacb3bc309070f9723b95716a1f444bdb003745192949454e90e90acd985fd82237c4fd22101885aa40bfe8ec68d662287529c76ce621224975a77c31e405666b790e647d98cd49f2ea66bda28991a60422ea1c3dcaa9a67dbdd2387d9335664b72ac12a82207ea1b15612db333c59158b3dd5372edc2198e6f56b93b5d50d0fa3a4ab07636b0227c93efd795d9ee6e948ccc590bc58e491a44a7d4202ce85e91591503f469ce7c404254408d0b29283b2fd63ba57d111e1836382c7937fa7278059f0e996c8653034258e108a57850a86ecdca02a267365d765c05ad73dbd5e7a9dd239d746f25d05a25c22fb9d449313215a423401ce3c0b4297b9889d7205422a0bb72342e6118be5b000711df918e7f12e701c60dbf0f65b76ab13416282650539d4e2236cf78dfe4e8ac7ba8dbe387482814f4413d3f00b191a48059ca791cd207c08618c03eb9babad3caa23b7131b9bcebb39a517690566d51b48fd1ac465226821169ae7e6d02b2eb2909160a7ecd272beb1ae08bb39d1466bcc84801698dc9cb84e4c980d6edcb44b79a6f603fc14b616c92aa94c3ff375b4aec736fd28fbf26af8a0150feafafb5e9ee6f0f36fc86dd6d28f6546ed52c54e4cef11d71d8ab8ff1b07816252775ad4f07db59484e3f79847445c189446df60a30da8834e01f113b96a60755f113fd3978e35d6f835be31ecba5a2cc16e4c298fa8d6087df95e632be01327e6b3b4fcaece3649cec3932d945852c441d48e6dd9e5ae88a153053671a52454a399f9a49ba208a6899c6cc3737c11b64528c4a8739172f640615e84b0c7a3f1cb24a11fcf35237b120f0197dc0a1a622724465405ffa57f8033a314d1c6a9244d66bb27810ff4ab80690574a5536c4ccd70eaa82572aaa3a840ebae591bc7616985fedf6b8614fd2591add5ed414b3197c0edb8652976b60ac876be16343d749dd823aa6610ac625051b272e9c3d7659a663d60ac67f34fc96998c8c19908657566ec54261103f1662f6800c11804cce999d8a01ee991278e48b1649d0ac46657f5b0b1a30867b8af451f9bec7f10184224477a0abf6f7622411bf8af5ea24f3ede64c096c4c31f8e993a20b2004ef2f6930d4a311927a3891581490aa46687378a20c0e206e980442d302b9135a6e6a56c9f108eb9c1b3376b44f2b346490ac16b8791e2c7f858688de91ef069d511542151c4d0de6630fc17937d88204c862c288689a309624f100511c032e66a0c0d057ee55e396109b20b88c9ef22a0515948e34696bc3245634c651097a474c88511c475999bb2d143e2a736432c326a41104b46c169841081b750d4948c9821448598d03c801d54c1d1096b88d87d8a6f1fb97375a93584a21e51e94cb678a730dea861e423210a4e6e2b854376d4f2832883c20c440b1cb2870d15232e762a429460b4744af230124db45d1c50ec20c04d0622bfd255a64a0ee29ac0e68c48008c3d630e8056b2870824522668f02cb4000766d926f01041de67c53c8620b2bf927dcb2b4248d897fa1b5832da4a17173207d5c79d44a2c217f60512ee66835e4a3ff193a90e40602a934ad677f8d043323f9a2da5c81c53af2e627a8e713fc38e76d6e5aac159150a2b7903fef406860097b78bc21a903222b14f5441fc7c68f1d73cd2c4d223acb45b14aff7caa0d7265bf0b3b98d340e910171b7408b7e40c2085e339f6f945e6ed83f7c9aa2deda2d390bd547354e91c0fca109860dfbd663ddb7e01c6d35423c35b0e1fd4e6b518e7e81b5af7fd0023874c7e424d729486b41a2aa6acadb4a0eaa42e1e5a6103b61152a8765f5c2418b86ef9236592cbe8dc24f6bee5425281154ae9dbf5b5ae057d323aeb5f254ce28374d212e61fb1d4c686c5405c4e79c04db91002ee6e0d4d22931834beff95d9605399f0383dbb3d26effed6fe9d009c0ead97db352eabe1d43e71c3c9728ae452a67129af16463aa0c0097aa50a72a2b13e1b7b2f18ea58d29d65c5fa3750a7841c5eeaa78d26541c83fdd68e38cf6acc61337816acce5f554c595dbcac8103c1f60ec148575487511075bc8996cc0c55186f898bdf75cd02df60fe5c9f92ed16b6a7756b3462ce2eb04447210c10a0261983c2572adfb80a30c00102a852469b94fbd0d6df5fc2f46f75a59036356e2603e354472992820eaf76cd551427f57da47db19240b05620796487eb78437830e3fc50537ba5e310360bb359d7f85d4be054c35048783172a8b6b6af60ac9fc970d1debfced5c42bd51739348f0c4171306aa1b38b3bf146380c1e0b95d979623aff970f0933544c7a9a7d33017d08fd9560321d23519dcd577c43841ed27bab68941a1726f2def6a266ed67a0b7141fa89760c02e01277f31403f18041a4fd22892865bf682a8c7d55a5781bdc370c5a23a409c1c7f182538ad3b86333fcb020ef893a623b3f83c52087c4d4614e40c8d7f17854b3df877d75cabe4636a6b49801b2c2d202a643b57add67911430b471191c6f41100f191cdd4e6424611465b09c87cbd50a93400d60479faf9e2ce3d8498ddede415952a15bf23aedae0c9761650d0afa7921e3397554f8e8fc0901f29befd86aa731effd546f30f0dfb561553d0e3740834283bff448849e4b397ca3130d0a1282f0fdd5b23dc35a45881f013fa28ac5a9c45e26e4c5493351410318b32eb729685df6caa68e514bf774a445bbd879ae84deb705fa6484867b085808f5cff94ee2c43311bc078bed59185b4f9ac848d901e264273a7f5c90c20d474796210f01228e1e96977d52753d30c60983504d1af4cbe58c924f84eb80066e605dd10ec5f1d732220b6b82c5a1cd54741b08e76477b2c8f27a42d7a4feb18a226429ab3bcd2f43fa899556ed5ea13d41c7ce6278e498b3e6ab4d1d3717258e8e6f4d1dfa6945156b51d9558564637330d14ee1595a596cd1b59d31e55f0d38ae94a3d484ffab8acbf445a15384bfe54410277c4878b9f4151e8513c5888433a71eb81f03408392f5678770bfb2092c3f804de8b1d89a057774c2bf89851667566292aedd6b9c48408260b9a1468ce653756984ad69209b0d097b015db15de56cdcaf014dda24cd85e2fd902e419775e1dc688f3b66654dab105ff387ec2b5d277dc01fcb40c6e7f7e9a76c52a5128d0122802df2532b6ba1e21095b919670ce1ecbe0cf389da05fa2c34184ff5211d1f883e03975d41124d7e1479471d27e4190a48de42fd01e93ad932267dbfadc29bc1f5b9dda7cc7dce77b15a861a3d655eecac6bd52b0af0ef21a5e3a447def15cc40e36c396a880ee2738a7dd5ed9277a6766a8aae80d2fcea1ed6dd720afb997f8534ee4e7ac76e5ab1dcb32bd40b80dd42eedd4c6235ecaffd20f9b7b0c1c24840e895516ff9d1e12ad7f80df4c8834d1d4d95f64bfafe516c1e12a8ed420411ea9af065ad62d6f93477732cd9535e7c5a339099d27c6c463302aff749f69bac30217ba78dbb5dba8d49cd823fd141d8ec53e872d848c33d20a26a5357d46444d6cc1933849df7147a0bdf133665a301b3a87d261437e845d28867f9ae14de89c514ef3ecfafd67194cdbd6167be7338a87c21606edaf1848a9bfc4829c5d64344d264b5ee8c36581609434560456af10cea41c32522f580907362eba9cb989dde3b6e923a7935401d6c32ddc9fa4fb47431de0aa3c7becc70d2ab727288ad156835298611c4276b1cb76477afa124d497785ae268e2101711c0737facb9df0d6608be0ec252d01c6532e6aea9cde5a07bf30148b3e326303ab133aa8458496786a7206feb7672f3f39157d213804d545c78519f0364b40e09850ac7e4842e61123546e8558e6167a888477813a2d14a8ee98c7dcd03776c81f2e42f0cc87a3468264f2b37e6271c2a57895c7debbaac5c2564704b0462b53dc1aad821000957593282f3920cc61e5a8689770aa292a5106345dcaaf64de927f25ac4f7fb22971d033f0ed2f0c86d1cd87e2f2bac209caed04725c44f6c25928ef2e06b4674532ec838524d2ba29b911ca9cf45919cff931d11cb3d1c48aece621c57ae35aa8849563ca918d0df796b1df807232afff2e813e3ae11f6776b8e4a873fe886c68093d42e0436789757ad85a29b9d47e081e7c9ade743617d0b08268b630f04dfcbb1561c9abc68472433e73f679932e60c963df59cb4f1a0616504134f80541577a69c0590fd40a1186ad7c9404a7af8f219c0cfe5be8ae11396acc34e39831e4275d34559c2128a0d7b027060152f692d4d70857bfe478638270b1f56e53069ca72b177de68fb3ae0022fae8e24278ab528910662bdf561f556b39c915dee61c1fca3e0d72f672757bc902e856d76afa71d16e02ae4b901caef7b98552cbe8b7ccd8c23bf0e6ae030f590e1a19029eec6827c90cf80ec36344167261f1d6ab2157d381b463cda1be889abe24f008b69e73db3fe12cd14c90db4d13be553939aeb2d7359965d901edd3fd2c364c784ddaaf516741bfc8f2bfad27faf078133a46b73596b8662014e8052415206aa156407478c81b0c8f5be086e5261c88187a37ee3f8eb78700c05b68003c4da4d8e07be0dc01b70e6b288ae2830c9c5dee049a99fefb69427e18bda783dfe082811d8a4df8610ee1ec96cd32389b4d660f3408332b9fd2939cdd5e3e7ec422bfb18238bc804b45a1485b863598cca04a2af61daac274bc6c5688c505681b3add217dc8b89141c687da540d0cdeeb33674bf76670a2b44277e01cf7fc24d3192f8b68d574d3a85797df25c4df2a597e65bd85620668b4c40998e2b59d448e8c9672dee412141318adcd19cf97877dab6d72d1da09c2444ad60df09f3750e2ddb7f97e72ee917f9cec431c2e50c5ccb25ee248564055f3ecd69363f65bf07f5c0b80056ae31679ceeeb30551176d32a5bd1c59154ba079c6e6ea71ed88c57228c6c249946327ce849b4ae8d9148faa8057814f14a03f1d7ee51e332fc0ae0086e1db254bf5d9959955b21964f3a989f48aa5d211f63fbc7adc97e81d013d0533acc183bd017f754c1def6e2ac2c94cb5b818be0744e80c3a887bc66924b89433c747dbdae1debdd9867736b065acba85054b20788bb4782cca223b41dab18b34125e136d885c04ff1a3fb056e9dc0558c31970ed46426aca02e57dc49362c4b31fc94f8a5ded1f14dccc1cfe7454e0320d5be674aa9b92f3a1a864dcae0844af5116a7b2ab8182d0aec68a64ee44c6ac29c937849a0bd9c34cb855a3fb5cb05c8b4cdabe5f24837963b24acb45ecb235a95778c7c1be61107442f5a994fb8bee2917cef97e1a3958b342faa4744c6595f122f6b7615bb2e39bce145c57f071fc31326364826bc47c5b224ac8cfc38239ee9dd10c81dce021728cb76cfcf4c6a02b075b73810d3b4c58a20438ea8d29330116769c08311485f1b088c45899d4db064ed94aa52939352244c9d110b619b613eb94f26bbda0b2e10e31993a618e2d13fdd564c4c4a7d789e223c7cacdbdaa20011bb96c0df8469e762d8062b633908379fa2fb394959971ad7d55d57547e1f763e5142367d43b78cda9b536a5c47fedcdb25108120714868a4b39e9a6a7ec02de50a9b887c78383f719c00ccdb3a22282efceb8637847867fb0be04db329607a4918aa3c71c164ee4717461f13307d110ad236eae80db1d5426020a7b0ae754359ab43b3723177f5ac1fcac4204cf9ea876aac67d2d0e4f59b795b1877568d5ce8371a72ba132964075e5d3ff8f7b9c14e0c1c5ce888e276e7466f8381428ec13f2d729ce8e4b30e606753137310ff9a38bf3c1981ce916ba71ebf4409bc46ed873553efde6aa587b2f002dc190061b98ddb20c491a0665df50954ea0b5ffdc8ad81ffe2afac73045f600de092baa94b1af79b03a694ab9e41c3019bc71ed90c1c3eddfa24297f35854d8cf94cf109ee8a8b330bf75127d48b43fce7a277f0f9e29644726e99a2b509dd141882828b35919bb1d5cd0312d9a8e3faa593a34633b7528c60f906d5b79928b441698732d0a58dba0b50bc6213f79e59120ca70ef646b7b47da6204a9bb530769927b39f48ab1888414417b833d7d3907c8e41a26c114936d83688debb60a1db427ef74f1b0b3cc5e4564948727f51c04a1f99a8ce90ae06da84d21e80be9bee1c8999e17dbf0e5704024793c0c0e10dc767e3a69343e90986154abfcce45d684acc5a084099867272d060d556a42d31266878f50d95336e1f2b4861d0f51dba83e1e87ece7e2b4555540a09961bc46d80cf132d28949cde478c1c7d3079cc1fd9593cd6c74fcefd037b89cdffae635aa8837841f36de62335d715f67ec8b6186ba789ca62e81e1e11fcc2ca2fa0629000873eaa1f49d190c294f0150928339ef92cd7434f6462d8e84f19ee91b80134f4b23e579f07657c29b2518fc3e8f7e7794f088816a234309cd67815247259d82a3f9c8d919965e3bc38caaac39d32dbc0c4b8ae954ede0f797a99719b56bcae48c56426ba468bb91f58843192dd74d9494dfc5b6ec602d6bc680682e5aab40fab3a67823771597c597a51e6a8f7b3932b05ee36b39ba0ad1a87718688ee94f988aeb247c010205c5bbd70ab98ca2305b715d4e797e6ac54e14955d776fc6e0c09d49e2da24aeda5c9bb4caf6246a8576e43a60b0b05be32fb3e1f7cb2d545d09daef65eaf690f0670e946a9ea14ea744fcff94f83b285009338403e8ee1519be4e6c38be1106746f0375e2712aa7be3c915d8942cfdf07f709a5965c04cd1ebc16417ad2f59f9d17d484feedc1481947cd728950930dd778359f91e83201304af00d0d8d0f27554c1ee1d0bf5bbfe454c15321e900a78ca8ceaec250a37fda9d98b9c8602974540473c88c850029583887e70a56e8d478cec723308131b054eb61f4f008e80613fd27b44200e592fc719ccf584484c259625e17263b2eb44061b478d73bd75ad41c22dc92ad8617514a63d54917c1525ae9b3eca21cf7b51444572f106c0d928bf2b401067d1918979cc1583261f8fc37d9677b52f3f0c7c2e1b4eba082607f80c6505f4c2888e137816120d0ee030cb3cea7f908b17e28c4b846096c61787343ba37311085c1581488410104725221c99edb5c44a3111de41e862eb5477e74e9a51528eacb183106642eeb900af7d21a32a5a1819725e0e66a712bc8171724585b54c93b2fc7b6734d43887bf81ea2d4ab61e87f92f3cb6be69941ca43c98771df2fb9eaa8fbe429956b0a6866fdf3f114c49e4df7f80deade576a478f42dcb23a0be42bf857e8d0c4909c7dac3f14675a2c643a56160206e7a398d99fb0656a69cf792b1a73f2403caefe023264de738584f537d69eed8e57f17ff1ccdb29aa4a39fb58e556b29af584698865554f43122469669628ab4a103112a435e77292b141fefb229dbe9c46ca9f453e76f8ae547e587c87f6aa6c9baa32788c500e4810e0e23df2e4adf0945db1a4814f28bd2a26b346ea9efab7836c16c66878d5adc0ebf03474d8acc7d28f77dbb35078c7866b6391fbb6807913843b672489d4ecc83e71f787415e4fdf68b8b75b9a87cda6e777ce5699dc86b5c41aceaed2a1a61de96803c9173ebe511b1626c8c99ecebca30d5a3b2c541c55d0d52d770d489aed2741606bedf159163bb23c3d302c47c465add6f4fe290e83adf5e94e10dd167193fea80db76aaafd4860bfedec747c174f25561b48eb4b4cc7b0612aa69f03df36e08e3886677e5be4eb2fea74c087ef914088a2e4c76d40a5c00eeeaedc11a0674111980ed6dd9f6eba9016c3851a4948f5a9885773e1494a83301af1380e46f501423a2526d03423dc45b0c8814fcbe7d0873a52fa10db44657529c8a45ffdc5f96e922474bcf4e00b5d720d16292a5a2da56062f862d90668547252cf4ccd5869f99889f9739dcb789608fe1ded399519ff2c563a7e59a518800e0bf3080acfec4145fb03dd87dda1f9bc606230582566f7d1c21f01fb989d23406f4db121a0d533aec4685943976c15a9fd16752596bc63c28a52568d00c9b061ce6ae5bbee532260bd6c844dd375db68f0c0b8a7952b20f188e24227450bdd47ef9e820193f154e7459a0f7fbc482a8586a468f14d165f2f7441643cf01197f16e694be183037fd278d7f9fda502d6e8140be75a4562f9582c494308d9ce9a6de3187ca05301460b2e54096c8773c2151ea315aa751018fb865f8de060ceea5bc97f77b127118bb4612210aea94d6f20eaf487eef5d7ee279678c1372e9a69c670eaf04ecda8e9a945bd0c2933babe3d3343bd13691faa24aa60129435d084efe4ff9ab17b2d8f8858362ce3109d5d367c732c96a04f41e1a9f0ecf7219922a7206c86476444f33656499f12532c3b86832bae82635ddc16bd2913cfe7014ad3c463fc42af0ce5b164882e4421e925232b62d48d47eb3318b31eb4ab3824d93805be7020579e97f05e106a74594214430bd0f3b5008a764bb02e7262b9592b93f1f58d04a98fb88ce8477b7541a5e24721403d6cc581c5a4e79f0d15df5e9e8d551ef7f98cd08b254bd93c2a40904386225472c16c7e5e363aa15eece65445261b79930bc4d6709bc963a1f3c6454a106734389e41e998b265cd01c981747b323edd65f9779f0e02030abfbaf5467ac2fe50fd15b01e71545a2a372725ace8e7f7576b1ece689a532c325bd3e9833422159ff791085d612858f2e0ec329b880f03793b85ba0067177b85eaf92696840c64128d282e7647b275e821fd3882e02ae8092710cf329769de62d56f85131aef85425c55eca8279d8e4ebfcd609fd0ad61bd4e7dc46d85c11d46403be95084c2dae894f219f54ce0e675c3cd779f562350198eeda82e2b85a2872193047f110dfee7b8f51d62510fabce9368162ff2be11b3709db849687fe1cb5411c00462b73ae9ad434f2a4cf934a6b4332fffe64e3e1614d82fee3eee2683ab1e56a55caf8f0732960cf2d92b97a5f314044e0b5f60d7bf94984698d7b6c614682132e842941038c963dc9b6991a0a526b454d818b8db19c8c435bda58fb161a647c46bda717c88f169dea051c49f20375c58e079a3ae8b72fc9f479606822bce738dabdf1099c45befcd52cd0ab250e0998b0dde278405ca341a3e13f5e2e4fcb17fcd0eb4594afe02ba65e3979838d4c3c66abc3a091cb8a3abd38238b27ffe281f4b1fb7746414795982e5e807aa29cac0e8c202337a1a172d02b3a88445b3507fc94c05a51c04d049a43f766555da52e27e09800774bf1c96f979bfb8cea18cb851bd160a7109a354c44818f3ba328622f9f4f4580b053f2bfbb333356d6b1422d4be3ab5b8e0df877eb7a41e5ee3382f437f05d9c8a29ab5e0072bf9c12e5f998ad3fe7f84de83cae23254b16212dd112b13eb98ff742e7ed9ae7561856d8d73e9be148228c5426da27ede345a80957eb080921c3e47353249329c719c198e9a5ed0090e9cacf1736dd4c6145db9199b25e186abf68f04c7eeb0fb40165131f0913ead8addaeb7bc5a28e3ceb081ebd612c34ab3a4e51bdb99c6d7b9947fe91714b89c791c39b0bdcc448c1d0c043b3b9b450b556c99eeeda58513ea86b62fe066017908d0f01634bf4c2320746ccdbc71d4c6a4e6527a5af2f421133f83c7edf1ec2395a511eeaabaa5b7b2243817033993da9d857d5aafee09ef807367c9c08e53ec8fbfe0d11648bb2647d7f7426e8f502095f3fda74f74a112348f49b89949bfdec164f25b37ec36c187638f1994c32cd3c917b61fae2d424d310862667ba637341e15d3581d6983fda58048edc0a6a8db984a9a92b9cca7a07ddec4c27741bc729af1b6c5f33e90bfa98b3d2d914fa002a00817f79e06335ebdcecbd0b3fdaa3d768876c7a3db85d25c237ffa61365b1dfc5bfe4c2cb4146365cedb4f843304ef44cec7036cc645f3e85512b7f5e97eabdbcaeffcff51f78b4b13cb4dd98af2b53e9df169e3776467b898f7cb35be612e4744b9ab1e2cdb406615e859c12e0d229326e772e9ffe579b008816e02a76fcda2ae841536a727e787f221a8386a937cd0d4863bfc561bccad3ee87e14aa47c5b1757eb9cb4446d77977512672022c848861c4163dbb531e34e8f6bd48bdcd3103256f156390e97c513a3b6a68b67b3a4d22cd9064fdf9c9e9e6921d217087eab0ff35331136d1c8c3d8e1ffc0e253e3e99dbb1bbaa9ac345bd8204ff8139104a0cbd28db448ab81bd013cfc858fca646cf19c6601c1c717bbe15cc64a6f3ca7c9d3eda1d3b2c713750ad6a024117ffa18f11319aaee124c435b611281c39e2ec1f8220ab0da936c70e8c4da937e363d01213d6125ef2054e8b6000d2c2b6f1b5ccee605b67eba6d5b5441af886188e6980e8b7c51ebb2fa9e8369c5f547c3f8d80f16956dc15d9ea8234462e37d3e8cc5fd46bb94cc541106e4627e2d851bb0da0407279f77277ef0cbe2a3a2efe828b8badd6d7eed9c366024936e8578b161e4f6af867c9c8110e9a8e8ca6b3f1efb1f8e0c182650bda884dc9fb8788353bf5946335bf44917807479c9ab1fa80e87d4604407d443f84a5f8ed62bbd53581055b9041cf2fa119ba7e0d11e3d87a61aa76282731e442ce7e4bdc8c1de83d65bb1579d308cb5ac099563a41584e0fa5e9992fbe99bab92dcd8522338a561eb399f7ac46efc765226981c18d8cd9587fbc969b40f57db9ff6ec0cc19c09bb8bbb37158dc49e53ac3f751c9e0cb38de2bbf7b226950ffd1ecdabddbf14f9a59790ba5a0cb5b809034e166bac4dd8ca08f78d2f978f7c6e79edfd43df074aff64cfac38aea1e4eace6f49303bda82c04cac34991020119da74a27d26a0dfb17ed3407c33e8d837461aa63bb2b26c62e646637052f0799a134f47632033b4bfc247327235d25f832d4b1c8c724a631d3b09d94c17cf5cf3195430244494064e491635cd5ad35c650280be5dfe47e89e57ccfb1e7d215fe488e7a103f43b5cb9a7182d65ea30ea1c587cb2020405dc613460c79a19a4186654ead6ca95b09f7297b3a9c622f23b839239b35cb7321c000c3d027d58b3897ab2e24a706daae9f24feab5da935013457252ac7b51a1bc56eb9ea01004b9f034c144f25d460d06f6218cdfa0f98daad2832bb9c5f48555888f1f9bc1169f590a716754d6a23da461d5e219523513d2970532a5be48eca2233827049ed7cfd5d50207f72e29bf339db38c21cc9cbca11078d82861528ee12587b99298cef8bb6af846799b3c2b5759c549c8da41450e1b18edc4b6b4a935a7b05cecca4e15f2680cc7305e2621650bcc0996a17dc71d95481e3a762502744e55256a00d8dca7ae43954a34200e70c43e65e006ce9f258f3fe0e6295288d92a70e07a4b3b6dcbff85270d82c7e41804c4a8ca9061d0d8fafd4265975e2464aaa444564c2a57724418fe826edf00253aae1554a0537157d30f3582c57d6cff3c4a8bb3c48efb8510420fff781087bbdea6a41ede80c04a707b9c3352f8f61fac0879cb75d3c1921d56bc497eef10ff91c804de4059fdbdc40e65a6ea182c2b4f7ca6bf45a1d5c7feb3f2596966c0180f984953f6081becd9a631d9de3ddfbdecb97ab3a577c5ebd654cfd86e0064e01b2783b2da3c8e6662608bbec9a63379603418867e1ba8b558ea9b748505986f7d7c4e454724978bf49570072598e18c43203e97b598bf599754e9688a0c927a7ec81adb8a34264a6c9dd29622a7244a08a254103f27e01baf0af9eac7f4b3ad19f2fd9fbfd74d4d8adb5fb57b6dae35acd3eca19055177ca67bdecbfb516ed2fb60debbac6b606f2acafd5c5f5864d676d4ee6760300c46c1cf5b1713c448a2647c50df3d08c0e4e3c36c9c2c2ac5605a9bc6515707e8c2be284cbd5ab45d208f7bc108da70c4be2d33f108eff61583589be9dc71925ae962042497eef45e6740571eeb954d4206038a13998be0343c15dd85556f4e1d268a76faae0eafa49ce3dc0d3dfe71ee1dad557bfe90fe501921c5f80bddb623f1b101c47f87c4e7ef514f28d6fc270d70b07588ded9246ee10ea3bec9859785b02209c51bfc976507586826e7d466e6e953875ddb512197eb0fb817058359fac56c6c3892e1044dc78141c72c988bc995e47bc2bad31df50aa783a9bf56f7e48e2d65c3277c2d87b28b82930c371a20e688337da4571f7038c5753ba60a42438dc69df832451df1ee3663c1ce3ce217b3ccf8bbfbd7284a60d3e30f8699cd70fbcc25317581cbbbcb2ca25d2110aff94272e876665507a913d9d514e4925679c9726a99c65db63000425dffd8e06d5ba5a3acd015e00c9c105529eac5343926ed10183422a9faf282080e4074e61f9cae63a6f9c9c1a9c6d6039986f60afb9e680749433b9463d338789a5bd4a570acd6a14355e8a5e465516185ab781a18f0346ee5f61f2911aa690289e5ba347b4e96ac12ec848b2d2c46b06d29b41fe625013c44720e3643d0c335ea30a01bf7aaab12ffe2a9957a917b7a10dae19797ddcd2a03003c37d2c51788d51b2934a8ba5109e3691f29483c309d3a6e3856444b3fa21e79551ea2c30ce455214ca05a13e6d0f520b0cbd363c2bfe69c0dc3a6bf4b808113c2cd20fe46483ab82c591111f1fe1e501b8cf6194aba1f705a86a49168c07cdf28b248b109af8059e958c46349f90a80322815f004f5c8b77307082e4b928b3f9ab684e20bdeb0b9e858f81b1ea0b3ef3c1090fc9d5abb505892bb53669c64299da8d6696f51e215409db4f6cab52351c949234fc506c2a650ad4c011bace96d3c76ec3d59a8c79a199d28df8dfb6fdf110d48f92a6835f1d2b28fc1cc15888ab24efc5f88e4ef68fb2f7be699d3a25081f55e77be500e9fb504865965a7d089344ae47c5292032ae86ea9d0371da45c1cad69a2a01a4ac5fab8ada01b0efcf778a800efe3704a2c034b2058424bd2f0bfda8cc584bf906aaa9618c8df454d4c1104a562923831c8f2c8a05803fabcf88747c6992588a4c78c5631a00efcf37cba55c863e9ee6a85c33942d9e2f55d004383368ce0038d2102f8c41d70f66904a0fbaf54f2f242eee48d337cff5c24ee507037a73bf4e2ff703d44bf65340d21a7667240a745f87680e1000f16be1ed50485f405093c6593b3e9a68363046522e5cd04863a2fe86d0fd7995bae7d3f42e1632155f4980809d8ba64c59a3a846c5596c8aa4739014372b06ac5c28620f0aa6cb7518826c96f627ba6b5b14dcfc9184624ccec21ca16653035c5c519a0fced1e96700816bda1f496a8a5a3368774dccf5e7fdff730470f16b54e1c714a190655660a3e8c20ee9402c179d1bd6405f15071689c61ca51b0ee4e79a85c97ff86ba5f2a86e9551c23fa22f5c3665550402d6c3bc208496009f06dd05ae701276f53dd4703ae09b125d1373c9f6fdc91c36cc33ab3a1a7314c973313fa64fdc95391566e27ccf7f56089566a24a97187f80a2519655bb87de857e5968c21b20b21d8f584edfd2372630e4f0a3a8b284a85f3890ec12374432b25540d35f5617397c96618816fdbbd06a7fbfdc905da3f2e4ec1220d551b2d8ab4df309299065c98819874400f281917f55880d467ce674312657f90a1420c40f10d19ea45c888156e4d472fc4037e6a57dc73eb9f8f1f956088b0548cb60fb3f152b93d22ac89b8ab0744fd36c532b40832062192d0d93da5a77bec6eef03a496130576a2b3117f5d666ae771d44e6760c740be824dd34c61f1e85f9b47ac29b3a9209112c7325f4804e771040e9c0b1988f39f217f3be3be64ca39456ccb43d40df85a3b7af0ed231871eef41e6e7dfca9874f17c235ca8154ca310f719dcfd3d582dc3488c5b5387cf414ef5108d192671e00608377728645789ce750d80efd5155bf231629151665722396c8d849bd09980d4f0a2449f124541510d1405f75f22513f480d18257a97cc51c65edc809b0e74496a7db7dbe79e87e5e8981dab1d229f3ea5247decaf98c42c72e8f8ea81849fe4a54cd47cdfeafe2e51c0be4511285defefbbf701bb55a640c89590e376820b794953ba1a093f7c5d9c16fba60324b6493971e5a05227ce2fd99877932b99f48db78112e8c40989fa82d8ed4cc860d36f3a13e3f6e95d34c6287609651144f9412d2146d6855ff12e215525b607202c8dd9a7029a5f5de43e2602221ead52d0c4d52ba0ab6241be7b4a8b3d92805d578b7a4d2856d243e8c155ac9dc9baca63ebd8add148604b39368a49427a8f4b853babe8ce26f2a47b3233eb999873034f0f554465c5609e58988f76d7d32f0888c02c0954398f8604cf2371edbb2f3e7c3f704aca9123724fb88dc9d96ed82d7c7cfce8b97df3686094abbb47ca2564a0f79f3631aa61818790acea6007813937647acd758ba4c38af1ac22a7f842ada741ea84b00dd36a031fcf4192f1badb14c28fdadcf5ab007feb2975371ece9bbb0e37703493596d2ff0d5ef911fee2a4309aeb7137659723805936b2087cf1c5672fb9c7e8f727ee554b570bbf6b1128d5efe8d656690a4c028944d02de11c2de53b4db7c043bfce565072c243ec32c8ee6010d7bf3c97df3cc6b896905b8a1311d62800363e32546de2a7f8d9bc16a949ce21f977a46fad23443a6d75ec66c9d56c7c3b9c6a522ed9e9da0639028ae507fa1f9e7a1c10c683c1cb362cede5eab7790ea0372a03649e8b7da558424e39955fda14e54550475c9021dbe267534ff7aea00d4f28e728cc07a2fcc3b40375c8d6e1ff81a5a082903cc1db849c90d9b3c5b69f74ee1bb72852f000486f55a5fdee8d89e1a4e6e7adeebfad9f41182eacf089019e3214db347e13841fc4eae33bc8e0c352ceffc95042690b4c68f8d0f2fe9cb6683dce62097eb22091a68e059e7cc735ae42a1a70c82f6444af2d04c43679d23ae82bf920dd193ca74b8f0389d3f69a28289d3fe3d72ba2aa0559bba753316bab4e5329bc75b7ce1bde31ef6041ba3dce4c2fa49875500a57c52d8ee9f64c86596ae86d029b17af5b4e3ad561382d7e01240eeb50af34c6d492258a4ca3539c6f2fdcc4aa937cb42a2a112f7bf00461e1d24ee1b1a24de4ca178cbbcb170c54306b1617140f958cd26e200b206efd147af3913b3941ee7418b144500fb558b4c797b9ff6e47e93ab5ffbd9b97216ddc6eb401d1b96ed722f7924e71a34ee8dd5df7411eb9759ef985e778ca04cc92a8e2a0c874d545e96de0ee1bb97dd0aa4450e02ea5d84794f8bb742063c1a3ab5b6c55ca0d951c4ba4010c38123eb99848cf3c91e06008bc5e87070cc41ac3867bacaf27db03cc571feee6f0a44e3b18042691c82b5794af358565cc17676a80628e452852252f26cf55106a600125c885fa88b6a8c8ad291bb2ae1642097c0d0eed5388dd0cf9717ac757564947bb71e9c2d947ff8918e188f3d1f0127a16769ccec4eb93af9f918e7ce0eeed3594ca1a7edc7339c1f1e0e951eb35441c484a0a15ea2ac67be4380fbe0eba122ae6473cc05d419cb775d4ff34356f59a5411439bf82b7d54a598ad45358b618ad9bbf07af7bc996bc3ece5930da3372ffe0b254dd2c93455ecfe6c463566989b92daf343d575b597ca5be47e07183211e4c415f388b7fe595cdf3fd860134ff3350164b3626d6f5b09adf055632388634abd074132e44d75169b17b09e15c3e0924527c2ae4f113f245c98f27869fe3193a3916a87d191c680b615087142dfbd6ff5c7ad28f3698c43966713d02e76636468abbfafdabdd8fa00f19c748f63e9633ced5d5b61977b7f87e891b6b32493eae8e38b4bd50b0873c4b52c8dcf9ea3379cd37587da37e37489108fd899ede561762fb5b0ecffc38fa5648569efe9ae70019d7d733a1550e6ca6a0f555813ff021dd5d488c306e6b586da679177b14eb184742b288d9a0921d1db7522edbf4e9b8ba02b49b1710fdae7475d1528c5b92ec869f1ea7393eedcf186d8cd5d3e2f264fda1c127d8a71f6663ae1ea7fb7113ef1c379c82092fedabf6a442d4589876c5326870f8afc6363836fd9500e14dc43938f70d278a0ef6f10c6090baeaf8238f30cf60eabd0437be9175fc76e62b465272cec00119b1aa33a43c189122a181ec85f6c4ec536058e8e1852aab839d374fc6f211f67113aac7282d7023ec75d7ca6e2b092ffaa650027896d725d978f780a24030a9077281cac57c24f9354d61707ba82e1f51db606e84216d1a76b37938a056f6149ec02be2585d1365914d4fac30faf3ce50a4b06e30da0899d280be36a14b6abb5706b1ac2bab504e09494bc9674043d6ba6f111dbe21a09dfcddd52bcdb0d921209e3390fbee1d3fe2b2f118383b802a48dd63be773764e7ce6f5924dc2d4761eb8589837c49eda981ec0d2f9633ffa0609c56ff6a917af0d0fb006a2b57b9a6b6aeb44f948bd4c95ae16309a7d2e9ed9793f9799d1015d34ce6e122610abcac36ca765e5093f3289bf3888cf4574d95b3d50dc121a1cb3a4ad49e990ef5c8844070808d753ba3c56c80aee9bc10b84062b77a2ecb312ed05aca4519588d5d0650599b5d00c7db56f96302cfa67da35b7b5ccd02e7ee6713d37beed9b2efbca005fabbb6378bf7fc5ca46a5ab6404c8dedfc276ef193c41d6bd4ef02c1f8be0b7074ad841074cd81ad53f5fba648a0542d62b4600adf774e46a1eba1f9b1f798ae66b6eac6a87b63c67a8124803830d79a527a3fdace01703da50633498154ee100d43efab07ac3c92824e00650a6b2d1f86d13170563b8d7cfa409dc4f879dfb5592f8586dedc82bc1351607a144e66a058425fd8a3c0a580f4f5b6a162fded1ef9a1d144712cca665c8ed8c334385194819fb94520747e2fc19497c81415650887c37833b0d6e2146f146ad379678d04b2839a1737b2f0e56ca8707103f9e987abcd865d72370610c783ad9be8f7abe02722fd0275b642977844cb85a34a4f6494aa3050d1bee1c98d98a2a0a226cf96fb853ca99e0b822e54952fe45070a88ac38d00155169786e3b3dc27033b708e81d4a6d448fa0de10a7976aa4e7821bb55780ea2417a4ecddb7577a8d4c04ceb4081476967e2d0a698741aa1ddfc5a910fb9c69c5425f2f4be29d024aaeee8ae5e0ca466bef943ea16d6037264696943bced0b746aec4f62f4c748df2cbd99596ee2b41a202122b9575696de2723a562c8505f27df90d858bfcc5c79715212efc2291432fc0fe605264800b44f502dfaf336edccb53f29012e6be5b7606b407e078e429be1c0b8325dfa8b2217ae3c9d7239456435f5df99ffbc8897ea4154712e39368c2f76619cf2e7024e49d3216c7eabf0cdbeeade7f21c22fa6a37306dd6fe0afbcaea6f62685b40ce0cff0ca818042aadac99c9232201b598f4a13595f5ec82d1b26566eaea365be001955dd2c5a113bc4151f95387614ec05de1b7fd5115ecc0577ec165712b03762827735e84c6160477c632da2ff54ad5975f949eb7119ad40c1a8a182b0aba3114c1a39b3e605e3d1fd0d5fc0e37eefd1ef8d0702352c32d718631fcd9180dfed67ba18161897f6324bf4abf5bd61e4b331cc06dc462c0b3d4a4a3dd2dfa21ed95fa66cbc36a34d73131c289403e4bf20fbab72e5153f57a74be0226e5f55fa032be82b2e229a84ec4a96bee9932f5f1638426890db8206b0dbd500921cc3fba3deaa9882cf1b9d88d16f01fb4b2adece19c90366bbd2bda21985b4104d8727215a86f499f35ac7391112f70011cc4e307108131523ff6f7e1ba3cbbe3f29580d8f4e4a87ddc627b2eaf9491d9127b68a626d212b7ace38b628d57481ef3cc3eb65600f25cd64f22f8400f3ff26c87f275013946add89177e376ca99fc032f1d17b90bb2529b5ae6ce7321fc8335416a296a49c1f534200f94701998ed75675730e1c0c12b06873b4a12e8e093176307bb652470ccc9192307dea51271c0e11bc31d3590cddd5a0ca177dc3a19fa81add02225aa6f2251db864beeda2331a25c760ab9fcf5f02b75addf813701344fcc5bcb303602164ec5f653222a77c2d2a34766a76e14a8afc21055968943afbe8df5a6e739fa611392a3ee68d7d8569ca9fca1f57916659baccd1abd2f89253590dead8191c6834f8d3477fcf53a190b9017be0da98aa0f0b506189e3eb2263f1c601642976133c1b26b3c3f31a516c4759e4478336717249d46f1acfb818d633a6db64bf16edf30d471c91d39bc9e92209eeb4c817156e3b8c0ad0a7fb9a6871a6666c8fc8155201f0eb6bf5c97e0fee994877b18098f95f6b7bf9884c0d1c6b40b83649578ed88c73f31940da6504255b114630af03e97620c21191ca363eb11530e82ed8b8dc93ddd2318bd2966b2df07e41b93bdeae950741a0d9466f0ac8e898e07fe27002a2f3febc9b3f4cfb53ecfe3b0f2bcc707bc1534307765568eec3138e0b2bd312bc15ed8aa484dcc2e73eaff13209a69db0dedb1d5f930dc4143965bbf17a72a3db87eaf97b10b0cd5f9f5134220c00d0bf2a95800bd2fcc57e100a237c1691c0bdd1d6fd6dcbe5f098a3a8bfee6be15dda6600003e993dcc6f0c9072ff6f7ce123320ebbf9a04e1764896a06558a1025b4e607ff14f1458f02d5269ad8692f85a5657d19dc2d291630ce122786e61855139573266f795b0fa5ff0fc9da9b85d1438b7eb0ca3c99d1bf653d4109f9f26777fe5ae8ca94e328a0bc4bd0a721ab7e584b0b4a11cb1e5e6a34017a2635a1ee95928a6ee69f0fcc26b3de4c55f7b3131971a6a0de212b1e000a4dd105b9ebc87d50017362085f47a4711bc0f6f4cdade121db4d5026e0ba57db067408af1eeff329a6d013f414d2d82c8e44c5a001424ac7d2c613023c2196f82e0dcacd02407507a6583024756e0b15ba1225816673741c0d5d0f1ab9ca6f44d02f82940f56e34b81d02505b1025ccbaf0ccbce6c59b7225412939d6c6ff595808dd084d610e7f9bf4b0da885e774ccdd36df3ba033b6d72dae24e69878398090260c12fe84866140d58750d9090f384bf1dbcbe1269dbd21b7c001a56b6b00bdda1d2377ef6c2e2ad9dccc9e49bec29ec0e4e56eba3fdf4686c7f7b6ea01443147af033bff2b13653b2a7cdf9f87cd7dbcf863d3ea8d4b96ab0b1b02780542e86e7871b1956261a24d9257be2e5070acf862faeb8906365853c3ccef8bb2e72945ae46d9928786740fd39826762d10b9b1141c1c72b7f608cce9636fe03c488c49cc4cb60b89d6bbff8ea93e9c737739b1f6582305fb8a36827bae06e57036f1a9e6fc52e9e1a83c2872942e694ce4cebda98f7319bedc07d2973d6ad2273c2f9d4e8f0dd2d00dddeba9cc80b481204644fab59bd502a7dfa91e43af31cc8df57afbb74a4bfd5222bb0832f58ae73f558c560cf7f8d41aaddd6b924e7043bbc2f6043a2770c8504c3d1e30d12ce011acc953eae5d6aea8e6a53e33ed0c6e74c13d51c57678ae2a24d8fa7a0b1206a3ff7e535f767ceebd090862bd6ce1818d3a780743f5764d2c61c28027f4df334f60261d40bf023a4bc02caf52d6658204e334e073f2fc876e564e2b58ba1204d1cec824f61898925431c1b9bbb7619d3f88f909784653b843e0a41403a331c71010450137673365c2bd59dd7c7b9ffa3d71270b851febbcf91b89b26bb046abbadc9bdb79429a5a20637063206accb3fb3a7a3d82ea4ded8b259b325d37e264d981c27ae2de303a24bd34508976b4d5470795cae2d71a341e7d1407863a8b323a32227bb65444e36128efe64387ce0f2839433587c90b9b8d186530f4eb0989d01028e2b5c742c92743b8d5b9268dc926f67dc9299966ed94466ccb8246c01668c16527039e385ab7c26350ca540ca1431ae5071311855f480821b185cce3c71cdb164db1425e2d0c18726618ca270f948c24d0b2f4f147104d314ed65d6a4cc185a7898c2069790125664b9b94187993a945c65cc01c61b557ca1834b175e1e50055108c028c20a1142b8902c5992c51057a6f0a20a97500e5c2435e982288c09c400218c27659020aa828c15577f91d923b484943123107334c61936575b51a2c2cb134649dce06a2a9c54a1225ba2828babbf8d660f7bc192840bae50324b6ac2d53f4e830b4defe27022265ffce8e20b0b681dc3cecbb525636e49dd8cf5cbb525612ea5edc5c66edcf9afc59d5f676d8915b52556dc9e6a7c1bb0352864b6a8410183a506c58a941a1419945853edc952ed8918b52760b0d49e44517b62a5f6a4a8f644567bb28469d6fe3227c789dcd8784c185ce3384f596ffcdf81e81e9b64f97c83ce43e983ce43c7f208bde01cde933261a294e958baf316204250cc4dd428e404277d8b105ef987bc0589dbb041c9cced46188cc84029c7c1e03c9ce4a40e5fd028734bded2294e485fa6d8c738b64664d73783681138cc5c34542f9cca11442e1a4af82188988b7b2a06f10b5c34701717375e51f50bddfea6116820b96808018e2b4f5022c43072d1c0b2818e1774dc7531a8837081cc4583a3e162f7c1ac061a60622e2a7e58b8a6d8778cafb9057bfd8fff82fd8f2fc25e1e3ccebf601e3cce07e0f927b7602f00fc00fe0503c00fe005f0b057074ffe0bd6c1934f80e7e7b8057bddfc07ff82ddfc07bf7afe8e5bb097ea73fe05537dce17e0f93d6ec15e1c3c08ff8271f020bc0eecb5c1b3fe05dbe0591fc2f39fb8057b856f807fc1c237c08bf0fc286ec15e1afccebf601afcce8fd082bd32789e7fc132789ef7f1fc20b7602f0cdee75f300cdee77b9e3fc52dd8cbe65bff82d97ceb49787e1a6ec15e3dbe847fc17a7c09ffe3612f1e8f807fc1783c02fe00b057cdfffc0b56f33f6f02ec65dff52f987dd7dfe7e7c12dd86bc79ff02fd88e3fe113f0fc3db8057bd17c0aff82d17c0a8fc2f3db700bf64a3d907fc1520fe455787e0cb8057b81af807fc1c057c00779fe0cb8057b7d2fe45fb0ef85fc0acfaf01b7602fd433e05f30d433e017f0fc21b760afd303fd0b767aa06fc0f36fc02dd8ab7e0bff82d56fe159787e0eb8057b793fe45f30ef87bc039e5fc52dd8ab7b17fe05ebde857fc0f3df700bf6e29ec8bf60dc1379083c7f07dc82bde84be05f30fa12f8083c3f00b8057bcd7fe15fb0f92ffc049edf036ec15efe15f817ccbf024f01d8abc85be05fcf3afdb02216187f58a79fbf02646e3f054cb8fd2fa8e0f64f0001b75f0262dcfe081ce0f61381800b60dcfe07fcb8fd43beb8fd0e68ddfe16bcb8fd2c9070fb816eb7bf010ce8e2f62fa0e7f60be1e2f6afc073fb15b0c5ed0fe2e3f6035161e7f6a780c208b7ff8430b73f0106b8fd2e2d6eff15e1f6ff9880802c6eff014a48c1edff01c2ed6f6171fb49d0b9fd3e22b34e0fcf15b7df87b863c5ed1fe183db6f802fb75f847e56082078b9fd3a04b8fd395d6e7f01c4fe0fb8dcfe95006e3fb9e5f6136000b5db2f8000dc7e1c2db73f00e3ed1f45f1f6bf07001027ebf477702372acd3af123bd6e9e740f458a77f83503cb14ebf06228a75fa33103fd6e9c7400459a7df464cb14e7f0f91867576d81a1eacd34f23f6609dfe946823629001eb68c03ae106acd3ef891ca858e7a60300b04ebf8b1eb04e3fd3eddb37245dbe5cc32176fdfaf5d40a9e420b55a04120be0b58311f3b2ffd484f60b946c59859d3ed1a15606e77ca7fd63eb2c0fecf9e1f5ad36c65ea24f9a1e5bad8d960689577583e3e1cd1f960255506fc744012873bd5650670cbc2c5082b78f80206cc0d2d4a5e48417b628ab7210a5af81a5caed9300448e39088f04030938d39c39a0d36dc0c2ed786d678f2b1ad0d250ddd6ec22f3eac1d5a43990ad66ab8e5e08d3609a8c146e2e05025b79a123037a196a52964bce79cdcced38a3be873dad8501ba6d37bd2117c1c4f47e9e439df471d5c9d575977aec2b9b2e45cfd5cbd4f6dae7e6a342273d52eae66db0d234ebd0ae2eb959825c45059794d490db70d5510eef2739747669dfecba91ef947e94695c2e894c6f04e9f09429cb24c5153d285e6724dc9152545784bd8b2d02c01b3cc39270866ce39a710de148f6933658bc28489caa28c0964b18904f34c2f98a44fbd711ecd15fd0f6dcd993154ad276b6b4853704e8c9d474d4d1e73229779142f4242b2dd9296983a4a47296aa3a3198d978ccc80a4634dd4321f9f1f20409f52c3ba910d9f64b991908bbecfa6d36cdc6a19ea8879cb6e72651c82bf2d794b095e998373634333a2c6d358326dfa70bc3a7113d6716e4c93ebb2ea5d74dbe8c483e593cf3a3e7fe69757071db1db631d817d9fd9e32eeebf3ab3dca905eeedec49e29dbcf3469ede71ed1ec7c73f38502fabf9876a853bc1fee1312ebfe9dfe9c576a5442373d5311559c2f255010da1fef435b72ff8a977705475528f43c08e3257dd832fe44a899ca4eaa77921972c1574a5c721e0d38c41dd83a391b9ea3e88e7aa7b2157aa888b62b13523f7dec8853929616d17c853a18e409e18a65860579dabb64578d57dad49de88bafc2c9af59feec5f8033f06519862697367d7755d37e9ff4caff6831c9784cb3527b2fb431a3f285df74ea140bdd48a88b92a11d6bf36614fcf29099cbe2e61ebe70613ad93f1ca3b819e5261a670bdce9341ac522b07da38e9656cdca7fb74ee041fcc63971ff59fd0e91b87d4f7c6a0f9354888e5893e2716ebab3fdf85ea7dded7915de1cf15eaabe8f7d975fa1d0ff22756d49fec0e9a14c8dea8e2d3a8e2fa434ea38abffbfaf587a87808f7f3b9f942ae1fff541fcafbf92e9cbe8e49ea9f7e8e49aea8c024e0b3cb7b76d9141027513f45a0b942bd27a2505f64f6748c57a847a176a0c2142a06e4f5e43fd25795fa21de9f9ee6fb5651c59fe40af8494eef7d922b34e310faf593787ffa29aa7854a5c621a7f746153fcfd5877a7681e390faf493d051c563c79c447d13cd15ead9559f2a61559fea1b552c9cde7b174eef8d49ae0c995f3f09fdd3bb50df7b6f4c32df438d3174510a0549b8596c819382fb794cc2df7d6241e9855ca8ef1f82fa31e8ca1c87f4f318c4dfa3ea147412727d28207ed5e9d9d553ac7335573ed239b26da89fb912d0c2b00bc3300c39c862cb300cdddddd3ffc30a49d98cc843113c68c2632e3055d8e17d249e76ac31fa2eb6ef95dc9dab1c140034ae91974dedc380f056d6e7072a893282cca00955122a24406113296a858a2628c9f31becee9a252aea3f3bf7f7463071cc2fc493db8f3cb71ba8b82d30c5b7eadd686a1d327c9271f4c1965f810e54249c307234adf3a49e9183ad9426067260c6d389b3a9ec149216ac616519b8e86b78358aefd40bb7c9b474805cbb51f8e2e9d4dcd3233c20d0a367470e304662411002098a4a4400c25628871b1a046c647415a41afd7c692c143ed7a08c31c52a0a4d106d209cc4cf612c64c08db1bb72f66aed96a68a17de44d8d07a3eb5c124a5d02bb8344b4956cf331dac8cb88e07346abf0a121d9388e03e29ab025db7c90daa88d8cdc66b4d4e53f0cadad350869c8c6c48909ea16272328b88e43715c037573986eae6fb6641b12a0d93edb67db50a7cf832d1fe37c586eb60697968cdc3c66a46f6ea3c5d6ba344f40398ed5228cf0c57357706b4eae8c9bad030389a9bfa118b304c66b8111576f29c9e8a8de6e37ee769b3de4df80dc6c9db7e7c6b7c2967d732acec375ef6f646969a9e35ab6d4454647d30580c574c2673ef399cf7cd65e44f12cee5cf424f2e9a6a6265bd35483bfdcdbed76a36066b32c338e051d25caa4d528b68ed2d19a869ca7f441327b98916c37ef886f446e358a6974e326902057f8f8f84ce123739e32dc117ee4c98ddc57600d3ffba8901ca708350b6acce95161e83387bc871d8989cdc94dcae93467ace3efb7fe31c7698239857f318fa6d1691a8168d8d26f31e7e1396ff356c999e4b900c4ac71250561b0a1451a5f8851c31d6e9dd452b451fbac00f5d395c9890525e3fb38263a3154be77b0a97e1e100b2c86917e7ecad8929b903435d5daec6ab10cef8ff9a5087784cb4f451fd6f147d23fe648c2e85f321cd759a4bbabc5f2c9f9842d99bcae01144866cf4ff5f035e0167d7f9f90641d141330b7e426e7f19a0eb6eb1fe43ced43a78aa2a18573b44f095c409f26739c7426f243275d86d9dddd62f7b4c3ce220cfdebeeb6cd280f55014767d3d948e03b1d27c9ea58e8ab8ec558d457ddb1090405eefdcf66de6c36432267fe48b3a519906f92b165cf7ad63f23917c66cdc4f9e3f0a5c3709ef973ce5a8dccb1245b367b6c25670fab65b32e6169b51af19ddb34a2d9fdd301ee839bfa467442327bbaf94165cb4455374dd5dc0fa9cf7d3fc79141148b9da323f9176f74daf4d95cf9bb660fb6ec5bdfb835c97053b38104ada70eb6b4965b93a7798435ac6f69f999f99b479f71d0b71d6ce9339f55d036bd23010922c1e4fadb9075655a1258275392394663dadb9386704fdb91e68a85e3a457b19c165bb2583b8f93e4a4a4445c7f3a6424654711916cc8963588349c64f2a569c362a9ac07d6714a149b2b9f3dd3d6832d9d88c6624e12b1406991d2e732f711450e5bd64ba7b01e7359499b44216222aba2e4444a0bd3d5b6b94aa261c83aca0a92505cff9c1da6513a6c5955f8dbb135c05b8650e290b52ab1eb6e412f20799b49975ec5f57728a41c81a5c8c8f6379193cea4763b505a172c2396aeff4e09aa2a1c27b2c705cb369780211539aeff0d8e9d1d0eb604a710b60459ad84085aa930f17c3ed7f15b1beeb92352240e5bd2fb755cd832f4f1618521ff7d15b9cce33b29764c6c69afb5c1a288314d0b9dfc213bbb2d7620e38b8872fd7172ba6bf14931ac6a3edff19dfcc2be9bb9e53369121c756cf680c0c4d4650ad291f394348beb5f52232b47539c4081a5a374c8fbee77aee7509c4bf9e8c91df223f7e17fc3bb7c06c5967ed4ee47ce034523d9921a790ebcf2efe12888994fa135ddd2b24edfc6b065bdf6da3c2d4e64444891397112a508a7f422b09db414f7e14f7bb0a4948eb201ebf4e8634b71d2bf9db4939612842d5d46547a1414c814d1109426943b3e611195eb7fc37d777777f85ad839da9189ad65d8320cc9b0748e895922d2b8fe3637f46f7857fd226bd3ed79ff799ee779decfec2eec58d730b67bc2d6acb5b1ce095b4499f8866e7857f7de3b91fbf0ef4497d560ab165b3ad1b4cd9e6ef6d0699d880e5daf1111732754d8d222998175bceb7ede9ca8b49d545af6d25eb7d3d58dd35651604b3a448728114865dc0d37740830d7bfda1bde8503ba0ff7e13f6db07ee747b9f18449d7ff0525dac59675075bf2cdc6e6a3d97e26e96d346a23302252c47df8a3d2b076075bda7c59ec74624bbe7995af5d47908873323333b70c36dc78c449af596cc967e6f33fe5334a413647b65c7f24dd5dd775dd54ba2292a0f188145bb6d2f56f25f79e8ff95790c988af5d8c533acd964d24c72dc1d29b5c77e76936aed7a430b9fecda49d8065acc76e09aafab98683ecf2bbac4804b65b56af0931e6ba0f5d997fc3bbe8b3726cc41f20eec3dfa3230141d07cc548d616de3e895cf215df0404c1e42b3ec18b91f3d49a13ba187ddf095ea8ccaf41379272f99ec0c58af3f016120cdd13bc50719e39bacf271a888f9d85015b564b6ed972bbfe2093b33a7026e4305a40e60a184e80a1e445680833968eb618028c922b61d86b74814283b3852d71726a6a4ebe5cbf01310588175cf79eea62113055ade02fc4c4f5e719ce2ffefe1cfbe70339cf0cacc52d85ae3f901ed6c245b27c2e37de844ecef6cee8ba40b99e65e9cc2d99367f3c9ae5cb80ed273df001a0243458c08782204889d771743a930ac596f4762bb0256dd079baa7ddf77ffcaae89802cd9d3f2e05e7e5b9fca05fce39e7841da1b74a4ce542bbb86f173712812eee55f3fbaa82ecb8f377dc399642b7ca07b8b1ced5f496a3fb8f8d92e8ae7370da6ef773cced261827bb8e9b48b79b3427bb4eea10baef7a9c63b04ef729356c4965fc74c6adfeeea98c9ac088caa88ccaa88cca6c6aed61936abceb10df7de946d368c6261233f634cd89b1dcd3262fe256d3eca9f9ee7d0af3d01c98ecbeab1967d287b6b3639973bbc966c758569a2e555a1e63d2692675de48895887d3c272df2ed43c8fe7c148b7a425a6af460ceaf14994d47c8f51e5028feff13dc624aea09aef31da1450cdc843bca262beb49b47bceabe1a7546f3a8ebbaaeebbaaeebbaaeeb7c7e800011299252c376dd571b3ec9e256dbc46ed25c9a3d42aeee6b4421d7649ab3a97b1a9b94885b38dd1803a565a0e4f3fd693c9d4ea7d3e9f47ddf172634c3fb689f99ce7a816255ffe792aea039839696bc3529d0f067f9d7d905cd0abc566bbff9b1a628ceaf65f8b3aafffb807d511f79bfbf9f0c3e173582a791da60fb721dcbd0ebc6921ba92f7183c7d73d9a3e2e8c759c92ca2e7d305b9ca48ee55266ae438145ee9d273b6de6249d3ecea3f72fb835c7e904ebd04f4a9a4b936936d118259ab169c4adf9f46742b41b8b107439d8923ad7d3a79742971bab8d08d26a4bc0519072956bd6e9d658bdb96eae67f7d33bab7c93f57163095eaed20e1c3b50c53d7d6e8a20bde9460ad27ac3719c7bd83dd7715cd78d21f7dd08763f27df69ab9396829e13b5d2eaa4bd69776b632d7371b7a2b33feb74bbed33c09e315b82d386724e6f8fcf3918da92bf3a6939061e419bd41756664b9bb2da54db03e75dd2cb3db348b9ea2437727f12ee3d5715f8f27797c76e719c483bb176757210f2f0509779c5dcc481d9ebeaed9a2fd7b15887fcd0f216906e618580f48449dd0b99105f759e7b5e7bed25f80089d27163956adf5a206ef905faf1e1c8f93fabf8c3406c63da7dcd50042809ca71d487380fc8a43fcf9ee795ff0011529974caa43f2d67b87d291d5f41b7690d698cbae07ab7d324d747559079fde7f591b20ebd1c9d37a6d4fdc01ca59cf7de08849de4285c3f6b7ad3c9717fa4fae4440ef8e558ce337f673228eb76b075b4ebb88ea367c851c5efb7bbbbab78d373313bdcf21c72e7594cf67b22a574877576aeb3ee2b6886ebef6315cfdb51a3eb3aaee3ea0bc96d2e804c205a46a562381b8dcd24caf1f53caff3bae994b946f29291848c750afb6e41190f5f9738d7bb5c731ce568fbbb7b2a88d73d7be7eedd8e02c58383cbefd41339d6e1fc4eb161c1b2250b1a163a74619474b2c362cb86028a19b5cd1087245925001902260c1a18dbade32898b2c95c5b83f164b7ac64195bf6963bc71b27274d6a0117c793d3c9ed4ce238aee3baf02d8b5bb45d27b179c575aea6ff51afebe8c4d202b79f134bb17b156e955a691d9d0f8e4473c81e6fb6afbc290d4581d21a4af09940b82ce61a38e2bcdeed536c2669539e3494ce4b63b38c1cf25473c30dbe5b50824fbbac0d7902d19242fa18a77ee8e49738975d5564d7fc1142048639e7245939a4ab8af7374e4e1a9b481fe3f8f8cc1e21e0f8fd04728d1c21bad3e7cea35b8257767454343b76274f59cb6e72e74f17dcf95c829d420148912d59483de2ec388f8f55bc9f60cbe8107422cd99450b97c54bbf3143418bd1c21b2260410e2be0c28a26ae926cc11b165872061b495a6e60728619356870501a1d9f1b54be1c9838f9a09345082e0b11dce7c6a532458c1164ca38014668e808238a10838c96249a5c258b892eae3f65228ceb5f736a4c5cc104edf444cf796a8e0b948f524a29a594529e49e797eff9d6518ebce19e4111e7299b09222fc0c0cfbc04e270b5099fcc61f91891c157fed508ad94fd3d366a4e24ddc6417ddfc7a9397e1fd31aef5489bc51c444d1a97a7fa6c6c4d1e54094d8ae8aaa099dec18bcaf5f7a0fda7c647592f49efb6ebecafb17766a4bc4ae9f396271cf10485be2c9cde0fa0f69baded32e6fb671fd7d76737615d4ffb847fa873727f956217a17eae79721eadba2c49ccefb20de98c38925784bf2825c929ce40b924c7550eff45ffbe4bcfe2e85420fe7f1ea29bc343b2cc8dab9ac33678dc84cfafc38d58290088e42d0c6932e57b620e3625fa1dec5d45882253d522376d0057f8a3280978757b1791e6215a277d98cd557f46bc4be5c0a05104a6532f59c3a725194a6905cd433997a30157451602a08bda8915690726494516a39fc8b98306a636a1a58dc59667e9dc3c9e8c491c5688e1e513c2464154c18eeb06aad0925f172ad89da052fd79a5881126dd4ae5ce185da2b5de6fb15da15da94a389ac07db900f3f44010712500e102163986a65a45eba6ec5d30d96ece8e8c3d216039c6eb025dbaac73dea445a2943a0b52d27a5fe48d6a4110cc1c5ec7ff680a0a5f4fb9d366d6f3adeb610418236ec5a7d3b916d6d39a6532cf3aa82513e4e91d9f58781d3685db1cc3da350e3bbc749732c7d54983e3fbb71f2f8b3fc2245dc1148576270dcacd04e4d6a6e5fce82be95d8f5c7c991b9405ddcb7abe6848325e3b0b62a96b68cb5898edb17cb11906d8812315b338f6a85310695f77cbbb91ba47f8e25eaf67872624bf2f6ac71d8b232256d4d72e5319c239d83d7281949c82781e5fae3e47c3fc5276bb02f641ef9c423cfdc7f5ffd1627aa9ef8bbf71ac92de9771f0307fa721fc4b932b6e43580ba2110f0d9b01a8124921c428fe47dd1f0c1fdfb42c2d4f40d7549ee6329e9767727d924a52ac7f5076d2aad46926ac78e24752c89cf2421a122f886b8b701991ea824ee961435ee8ccc00000000004000f314000020100a058562a1702c0c7565fa14000c7194467a561809a49124c5711404318c31c4100008000010630c4106a95a033b4930349d4cbf693228d184a6b8f6265e35e4802ad220b59452ae9511981e4a674b9ea234cdaf3bc491777b89f8baae46a55a00c7993710551526ce7ef2ec782a6ffa68238125157b1518667fa498c8584b1592f8bd90680f55660f1a8aea70a9b37e29ad06183355563dfbf8569920889e24fc19cf2a55c325a0e9df81c1533e3cf12d45e283871520eae7f73f4f798eef985770f1d69857ebcfd35d0b4c687035ad8f793b4f20ec4ef3ca0edeb75f27e3383d0412b9a5e590f3cfc4fe45f04e0235c6d760db6d25b2156343111dc678ce8c871c93e266fdc01767217974de4293530a2f95318420fe4cebedff2998e2dcf4df01b8bb726d5b630485815dacd4118e51217970222294a1540a9d791297ea2dfcb36f779b3a6301245c4a195dbe030a692d7d2651fd51b64794c56a54b3212ada345782215a4a76ec864d85ce4192ee2ba8ddb877675604ae54537c0cac3387b419f8e630ad856c86fc36ca920c35864b719e862b82f73811b2f3a0b2a34dc6698f9b6afec99296011a9014b15e9e56bab830d059df1ae6fe9893612fd8c5d4e8578bc8ca9f71d00e49e5c62e03852409a2e5f3dce86e706bf7fcaf31160976828c4b9feeb95d1b58ef9da53bfe6ab9011d8bc1f32b85d7c60b67cab437d909aa48b64d6b2a3f701a8baa72d55193a77e04f50e2fd58d632112a5a7c1bd2ee9b8e7d731829dc1e06f9d8fadaf627dae8d07fb7be5c39c8c30b2421dc1ed22e46bbe7ee8c98e2b27079b17413862be32c6afd6f9086d6b22c5c8dede947f84e56ab0e68e46f155a95ee4f9a752e871a9575b20e44272a332ab3e3c373728753dc249ac7e93177c732e69a08ea4ee53e5b968c1ed32cf376c9b4e20e9e998069c9d7754f98124e6f4f35afc8c4d3c8c05b96a51db775ddb2bc65bdf4f2daf8c20664460806acd55e65e6a35c95ec9c618a82d971b7d8a43af9210bdc5fa48b767fbd45ea7314361ead9fac7cb1bfa20a62a969666a5f3726c8d63c7a12636102e0b9a4b0d053e421a7eb7e8b86dec2ac343b38b74875deebd7b4c8fe4c470529eb5b5c48539cb773e907b9e8be37189ebcc16986fbdf81e1d81e42be54eb6b6f5c9254679d023a44c9f5b6038c59406e66826437bb5f9cfcaa7efc52f88a799ed2a6d1b89b3019610c0a15722ddbfac56440a6300b49685e1107079387f314b469c87af5f92378e804c054f05839d65bd2a1e6f83a59d4a49705845327cfa09ec530e7f7a0ae593c6d87f5bfb39389009728ebcdc94c291cd491a732a2a3f2e890e4937b6d3f859c01d720a12efa1844821b78d47bae3b23ca17a3ec9a6cead88f331839b2cdaf37ed4f540f023817425e1a8025fc977dcc011a4f5e6619e5b26109efe8d0ef2c108a7281a850009f6b42d8f747b87d7290af3c897eeb3eec778e503248b03b6b6c81e1b1234c24082d9893722ff573bd61047c1102112ac875cccce861dcf67cc189f45db621d9ade8f09194c3cff9a52cc2bb3467abee8ab6a3bdddc1ea8814df52097e95210d74538f28b5c1c979e57e1c1ce14206942c18fb82b047cef9992163222cc53b0ea2fc7c9e7eb8a308b72753ba4069bebf67f1611536ff0b3e6759bb02070e892c40e060a86d70f510451cf872c9c4724523535e800888c3fc3a116e05bc0812bc8718706f1fa8c91880eafe24520b130efb0325635b69d18d147e09c3275234cf415f3220ed9fa20e4a4fa7e8134c4f0e5396a1ea9f0bd8fa6be43e484ce47a365496ee07c6461f35eb9f57bab2b1dc2d235cf8220263f3d59cc433e0f1c80b2135b2dce31dff916df6ebff100cf1a466370c495c7c3eeb37aff233ae29a4a831b2c133a3feb4a66f06d4f053a7a02ca8ccc966ab244a4a7c1d00a3c86178f47cf2cb6a1283927654546e2c4706b1947707ce3429209ffec304984e4f1332b409e8ace2f477aef50c568b54f97ce58336b6f059c83d2e396c7a2f21888f961e1422005c56adfc16341cb4a9a822246b95248e9d1d76ff9fd84db9b19965f4e906cab177019160912ea0600aab9fc079331e052a0224ec6513cf729149fb62247fdbfd779fa09da41945580310d4d50f3c62c64ea5dc5835c8a1ec78fffbf298e77da9e58674683faa24bdc74e87d03b2ca19fd1ee864c130fae1944a853a14b5529010b9e925c86f2d5ab724b99010e96b8eaba9500dd002536ad7e5e494db91b94c21a621a7e86380b4b816bd63f8937629ff91042ab93ed103ee9ea15c1559fcb59cfaf14031091942b48eeedc3d49145dbd99f1cdb3fe7ddf6f7249bec266c601fbe58860f87b7754f9af43cee0b53e4eb60524c3d038512f0bc35346a4273a63b12528f90f941aa528a1cf13702057af2f9d8c7d1bfa440ec25698a9fc53a0683fb088e47df53bc046be8079b48748024b4454f4929e05fe6341b9892dc24664d2ec6d8c7ad095ddded0623c467e5714e050fd23b755301c641d52bd1dc7e697d9ddb798016c0abc2b13bdcec6002bd7e3f3205b2ec44609829a53fb087179b18c048a9374b602eb68eac26fd917d0c24abf4dab13bc54e40192839d460e60e47a3f5347b1293f98950b8cf88477e35182e5636f8b25bc9218204d79256395cefa50864f641cc553f225b3ea3b29b7335710cc311cb2739cc6647d245c929c0b1891e17d4acb57cfc1116e272f27d96fba770ddd7e4ab653c0fa688e53c329cfbbd5d656074336d0a8eb78c64b31dc771a0ba47700be669a4e2f7d6b2ac042ad5b4c203ec642d99327a6ec86e74e45c0ff48fc46884dc8531a0b861b078f2646af944de9c0c01c984c135558d33bcae7f73315e78fcf866daac0e12af6b16446fcdea7f38ba3077fd614b9eb47af251891b11be521039887c4383aef4a22c221081afff34c7eaec12ce2289e0216e8661203d71fc88676b5ed5639ccfe382a5f90eb77443b921840c81420e3e6d2692b7595045c57539fc04ec3f1e3fa837a87afdff6a38c94173c44b92a15097337825e3162db435c599e47c6fa0f804c68827119a9fc275c04cd9348bd05fe3f96961a0bc08bf2215180f9766ee9e719fcf10810c4a31cf4e4b3243f4c64d3f5bd6f74c69e20043541678977b8aa757b6e819bc20ee6a988d991a0ce26e59e3b2af0825c460b1b46b85ffd690a1e0f5db7cd22494eab5cbdfd42d18a53f059f47c98a22386b31645f4112b190bbc0fc1ab95c15304b0550304f5f331b197293ce475451c598a137888c2ef48b1419dd922fb642737aba95d0f4702e931d7600543e499e9e7de31ab683f01fd03447548dcf25b50ab187a6e23a02b8f9dc0a6fea008195cf16695142c14f53fc879628ee34e883d3e9903406fbeecc558e35ce024c5ca42765f9e6774665360ad379882add60e417a730b0638b7ca06a1b69ee825cfa8373114ac0050c41f7ca48eb0e3b4a45e6514e5abb948865a0301ade0a24bd6ac0b3e0c3dc42012ba42cbc101346257acb5920b9a16863fd5faa3cae07dd472ce0785f70121ddab9915c69055f6f2278023fcc66c6c810453e4465ff8005f20d5eaf5f5813ed53e6c82f9cc8d112f11e11a58947ecc0afd3e54466fcfd8ad1cae4dc3d1b5d498c20c75e7e28676d106f7d6b339cf6afa407e789077362f331a259bd678d2b9dfe420850343276c4e2cfa379a724182c64aa201d8a217cf140b16454644aa621fccd1b09fdf2b1d3dd855a42e4a28daa6a7fcfb61cc31516532c0568b2f66214ee932497b0653cccaeb1aec16b2dd13698cb441bf0cfb9e226d6882ada33c94e3552808ab8ff91b4549df5338b6481b744483531ba8370d21a14177e498968e2fce14e4883a210305d43e23ace43d9136becf66d50e49f4d42c59e0f26004b4079ddd2110b8d497539edb1e82a82110f35117a03b6baa276ee09f461b74f208608b05c9be10d74ad4b1ed2020013d60a18a0ec9f66c150686dca7438b2f65d15491529fc469a5e10952dfe10b4fff4d1b669f8bcadcbd63f5cdbe95168f0cd0852e5f5e1203b52c4d3987cbffa76e518d46c9e8953a7996f419cb2f32204075c22cdd4654c2d05cacd2e28cc1dfee302dcd362ef09d85527b4b879ede80a9b9f5aedd41c9fb1eb7af8dea2a5f748de44e1e94b0ca83c79a274cb894d8bd622652283e1af4c4d7814e44e42d8175f2769da2f8dbc1ec3c4c3090f888004f6585feaeb448d01f2d56187a8dc2b69eda39fb22445328c8c050d70703c5f2792a8aa6b45e9e36db8b0aecba7305e0fe172d85c07f85b3149d43ee77d55a01194e731026719eae5ba2476755df6c3b55366db7d40dd6cabb7d71c2672bebdf2dc50d7db306844bded27dda4ea4b1a63c0344e4a54f033c7b206b8279c2be2eddc81436fcd0d840ab462667b66e1c1a914c0c576df3340df1d331a317b920fd887efbb2f454d5ad678d50118ffce8a5663beaf001b74ac7231396771549d603d7f1ba855385eb015b06c196f7b960bc0a8a46029d84032c4700355ef8b2e703361cc84dce665eff059bc9f21c43c855e30148936a0b6bc67a31177fd37f83095e9e802c4cc4f3030221833510101f6c310cc57e7230108e01bf6b202df5bd043ec7804c30e2283e27219faa9a3fac3b892c1e79d9028af514fdfc0cef26f8e3ce9f91259536a029724d962e0e288f0108ec683ed07052de96107e1b4c7a191dcc3bb2e04d93ce1cfdb6b32166566ab0cf19bccad9218f48cc0bf288da80c9fedfaa94d31248f84a48061231e6fafd47649f584770323fc14c062903b717a9b1ab042e076c8dee562797d662dc895a953346dd3a763bc53966d10103a0944c93f034442a425fe0f53058ac3257189277cad4e16c465d2629a08c067cbacd0f6a73c9bd10a9b251dda8a0836058372d9e0a200f0b342342c8b8780e139ab916abedc9d623b8b5bd7c031e47fdd2fa1ebde6e2475adc0045d80d58dfefcfa0a7223e269fab01511fd100c369cbf1439741cd3b4debab39ad774c5862d0e5f279e97618cd734e54260703d76254bbe84655bc2c90d12dc1418428c7af2ae406fa7ca11296bb2cb945c89c45a251ea66c2ee960ad839aad53c7c504ce24a68d47e726ff2f095eb33be74bedf8bde4aa68a98efbf106fd765f5c5292adf6d5b2b5b83332681514e4aee4999f322b0f5176b408d73234f0a2e68acacd4cf91245add438285935f055e68610bb202963a2834bc652219fd3e88db88fc8f7d6cf4163cdfe468aa9821454212b4246f7d3b289e61432a700316c0db470d08aa5a15715e744e8082d452809e92d56d0bb03b595e28e94631e89be22fd596bd585ed48c3aa80dd97e92af3b2f5aa6fc065811bef6328e9373bedd542a94588c11bdd36a06679ea7e123d844f3d74e6c8f9ca51b1818f2e574f15718e96cf3c0be5572cef3352bb67e676de7b92b761e4beabd2e9a95735f6af76dc09c5996e1077bfb0d84bcb11001363be811d701400beb0ff2b68147a56e7a0f11c07f7d464100ac917255b15bb4ba7adc14ed9421ea332806198130fcc4a86e448f10b015697ca08508e02b43da6260dfe48d1020625f82340100e1862afdc71992de7c1a84006c21f742945b014a800cb25bc0029e79ac11b83bd986bbb05c2b579eddfbe7a4167a6dd0612f000fecae10ec6217f8455a8a617f2fccd2eb129458d17373d1257a8bbc4ef6250390b0a3a09154ec8f325086c66a5b51444b89a1b709ec1e03ec6a1eb81c62e3315751da1700b1c599ec359f56a644ef2c8ec6dcc7391c0212ad8fffbcad4dd322d1d3c66b6e185336979771c528debe620919ff14aa7d0b4cc5445816c084d6d91a37b9c197d221e84b082f8b1490f50297094b1fa29bfc6ddf6621779438f7cef22ea518763550e7fdd8bed025374b5979bd65a1df637b5d7382380f9b3b43c7c70afacbbf701083fe49767c31aa1ed7dff2843856bac1aa1f526e5ce4632f08fc07bf963705c19786bd87562cd962a4a7b832f37b110fd0cc99777c008e08d677cf038a8e218d1847d66e95635d2a6159b2dafaf61ecfb7a9a28f3cfe030ef0e5efd8fce77e2464dca7bb216c0dd19920ddbc93cbf858c470c04468ad22204a17238b6345b4b14e791d33ec8c7c4af0d869789c16ec0b721423445ccdfc9ede7c8490936e3a5c634912be8e63e0c3d48c9b1eb2c2c6ed1ece43476601b84983558f1d9f5a5a3c41518c34acd6077973ad8fe0418394181f2addd928fa5f48df43082e3394bdb20d4f3e04e1e42c4952ab709151079b1de33b841c057d10a07f03d60a44b76ca0004d363abdfdb094471855a8712a951d9bbf4919b437db19c56354c5231104d5ef037c3138dbd2ccb31001fb2270a29cbfc02e03fdd3a2bae916935f0060a76a7ec2d62468eca90258da80e54e95997d00853a24b47a41b6c9998e803f03aa3fc0054503d29cba574c1bb3d30841aac843e090a235b7b8de60aa9112a6e4b39fdc211b1f56246ee2d1538449175335e6c80a68c221d5a38848b3be2f52001cf945c5804d9e8e3b9c816c06bdf8ec72a3ba418847d4aa62cbe0b8c155a6b2f0c2adf2f7dd8cc924e24575cc5bc07c3e53eff0042a1a1d9f495e2caff0ae6b94af66b44b391cb7017a1c70f09aa5d46b033cf67a2cf1cac156d2e7689a01334133d64dec4f9ceb05e1b137a8ad39945b985a5ebc685edc2448b3eb4f6f6dc7e8d76f6d5154ef642a4e9d9ee9dc7461a3da122e3d0a6735887f6de0ab07439f477467757cbb0dc2c4ba4590327e068d6c4cc4f7cfaabb0e0d1fc686b66e0904a886398aaaceb22903c99782895a6404485c139544ee38263d7f1adad96f3006e00cee8ee9d3095af4bf8e9e34b40f70ed42bbcf8fade09f8a21147c5e1b0092885479b26b1a338376ba1329f3c182beabc8d1dad253feeac58b08052656d8904410c1165f6528249c8ca9d176a98186f628aef75a6d7a7db9c08fc8be4ade006b8467f490616789db75c4c65d02932686f324d94b231ce3515f53a129291b55f31d486939e69be53d01986e51da15fb071203ebae7a94e8f5365f431b39c2d0a479ee03cde42ba273f62322417fa95eafeef468260e9e144aefe23fbffaae2413349708b2a467f9a7b9d0f12e7e9a7e03397a02ce3a14d74e4b1c119c57f4078c36097bfe3c3e122d02d56e337223d5e250f159e7d52370119c9308e9b8003a5c3dc18b332b809ae052f99906e5b2c27f8951103fa15e8ed20d0c79b6666d26afa602ccf3ea42b4fd06708dc761d940ee145fcf1cff11dd4395a6323d8e30dc91e8f78553c5395800312cc68f055d3b6012c815c1f71b0b94cfcc0b041578cfda02c1da7ebd764ca7b6ae7387be3d9ccc9256463aac87179f7c6b2294b30d1a48366942c9bbbfdb9909192a850640a6659d4917f18fe3348b26634965748b9cf065f4c3905f083d08f79459ace1f0c634e1d291a17bb82d76d21988a605029caecd9631e476939c8211efee4218183781f64acd5641d9bfa4cd420da95255a66b29f25c2aa92652971ac2139b8c7ace5edf96e659a096b77919c65901a0a74d5e7eb0095246a90e972a2666c7a0435fa005b4dcdcd565f9611c903c1db8a679c347656ff9af6b20ae57560d163272cb4deab1da8c50d7005a663bbba343a86f5185d5a062886ffa938f1b1bb357a6b517a2ddf69304d9373a57d1db4bf0434e84ae25358ef1dffc6f051f7904479e53f039687288ed247d5ff16491363f81dcd8bf53200d90877edfded5926bfe48b1f27968a47cd2cab8dd62056f87f1548d5224d9914ddf5c0336183d6370c0879418565c0349d78596c1dbe76e7f66b39f524a1343ebdb94f71c1d113444b1ac716ade29d02be3ce51340ee4b67db039496ad27286624e3fc4b66ff4c00478567c9068dc68bfb70d16a1fe2757cf2a366f5965c84b13e48ed9f17b95de16dc98009a94a6253af753e938b02a723cedbc9b668f1c7d1af7271da00c60ff3b3a093b2624d910b7cdff9d94b57a2f19816334783a0d6a5dc5544137df945364d211bca93f6b150da49a89874deba578909145e0dfa08da916a3a657a4e9402c6ebd1a234c012b90a5dfbb58a98a607401639c91b4d1f71c5726609d59cb1ba341795e729dde6842117f5e058790e19af72c07b04cd955624b65a3abe1d6d4f65d14d8360dc0a434405cfa6a5c6879b6f1866965a2a36925f0a6f89e6d2e1c3e3161f45469ec2ad79d452101079bcaa687246d74b3b5b39de868ed90a72b30d7a8a7e888444f9571e368acc4237312652ba8f17c12ea7682568e86f935a15c6700c545bb2d03baca929552f44835347367c5d6e05c48e9d77dc767494d80cdc8b022c996193b180eeac6334eae22a7c72bc7421a7ff48da49f45e45d3290f0635f8ffb0fa531d9630d822e9ab93aea61eab3a54f5086f3d8d9695076c1aa1f555bd529a78ca72a17c482ebcafa08cf421990aae96d62e972958b2378a6d3fa7b13a7452e6095f731f85c91fab59837eea48380f832dc42aa8041991de30846c28492211324fcd16ee024e8400f2aa30355f6b41d94c093143148fb6dbd15c03070bf42d25ab28dd84ec80c4e816d3350a05ba76331ee8d08e87f38d3e31f8999f7a89b89248e9fd066c24ecec64d4ec8bd1327d46cc7c1ce61340216ab181bed4a0dd382a26643cc861289c8f5be28b0aa43da913b230362c0c015888c621743dee71d9b54bcd7e219485babee713b09dd9ff54984c39d76ee8c1f5ebc2dfcf541221adc9fd04f82050168d46125b6cc94a6000923080cd98d437cecff86577082fa4d8cd45e9ec0906a9ea0d47b2158c9a099d25d6095d4da711704b12232c4566581f1e742ce45a47055f50e6741ec7e39e40d18d07ccc0ebd486a03aa192faff817c24d00f1291010ced6602a8d6c64e36bbb52bf7c073f76ff3ef66042bdb2ae06a20fedbef961de839aead0d2ba676ea8e6e732e0e8819f74cee2c5d7e188f38e07327955d8f323d368c33b1fe74c454ed30041a8bfcb9f74d9e133f5fe982c063c3d8a3708dc6f846fe402629d799480cb57314eebbad8326939790a8c3602375c6319717962eaeef77e63a6b6c30ffb0d3efbad7160282c37218690de744966074479a29c1d046661ec7d1005c416a2080ecca4035b799f0e09712e3df8bbfec0e71a9a7984ba155a6a39494207f966e4d0647b4eca0297677175f0c8e04d0d982c387224240c8ffac532f17b8b0025f842eca6d885a956a5b64384f3e6338c1040861f759137ff897c4032fc1065dca92cc05e8916211347bf398bb8db7f4f22c50346b514c4d20ce5c600f5b46e068c45083a7d623684acb07ed2a0abe08318c9ed274693c28f8e0728bca9f5fe0106e0e1ba58b39dda619d79dc945734f41b7a0648a78ade24805ac5473bd5d473e69c485fd098eac0fc5b9d0d0e50e4c260e0613dc794f95de50b44058347e88ea53f9865e2847e3032845186a6ad8461d0c2cbc07cc22a31df05815a76470e1bfa999496aaf0a7a9f7ae2408eecf78b367c6061c1b9599894cc723fd46658bc0992114da87cf6d48a79d79e4c2f0320fd7e03153a49e142f05b4976660ef009341340c14d030e40cc3cc1708e25c0c802f41f6024ff78d0cbd2973410666ed6947aaaf53b060e2317a33c51a99270a11b5b54a5b3e1645e0e2fa1760a533fd29adab77f253b63ff0f1025e36f424053faf2ffbb1c1fbfb8cbb77e1d5b085e91092c6e832551da518ab11e677151b84d7c16a505d50863944e6e5e586c278e4b4dbd0d288701007dfe8bc24aa7982a4db273e162415d0684e5887990515839155eeac87ea587ad6a1a044d01e525615005bc1ad334ba592d51ae0ac0d13a2f132d69075f7f64070381f7550598c7c76f8a6e89ca4fb6213b6c780ab00500a7c5cc39c5fa4686de2b7c5e8f05d27c4e6eff8c2b92f43f3ab9dfcf987bd5b09d9723fdc779eea3cd96aa309117ef27b9cc2c4b2fb08717305f89b3f368fa180e96ee3e459552726b9c8d0f4b1a2ac6e9aa3e8497c83bdef334417926429c0a165482f103bc04bd8cbfbffca5d826833582d7b593dcc258a5d54d8ca17153339f909b233a5d3b9794c250b4c9b4ca6105b351e2a1eaa74fd2bc7f7d18d90c65a43e67e527457a4801c0f5fa3539b0bd198cbce62cec0a4c804c3f312c345eee8518c1d99024f79b09d518d8dac8cbb7b119ef97937bdbead408fc74874d05fedea71813590f2fb76738a505970e40e87f9dc4c879fde4689aa8045e45c36e4956bfc9e9f4a9c4229e4a351eca4db4c32e5206c37656067d1d95cfa911cd99990ecadb40bb6b8badb964842bf33f66fc61f07624debd3d196b4b5f029bfe2306db06a7ec83705561eef7b5251e976022c0842a3080ab514dca374bf93cead380cf252c4fc50052730ad15d9a5e96fac97a6841e623bcc908a736b38511c7e50c5270f10b5e709ffbfcf3104403936a92a8649132c605b1f4a96ca4c0cc86c02f7ef506fecc6d03fb5bc05c67e02f22797deadc3b7b0e58c3d4ae9d3b62c43034897d9dbba1874f2403164885675888054ee1ada96e857a41e8322ca2d715d630f2483f9ae895f7d335bcc0819c78ac30d64fbd7e4098bbcb15f1107f849384648232bbe342100364bd3b165b7fc3a8490958c76f0ec20f7a624bcf7e9bdd0ea32170c5891a03249db4e1e0a11e1ca5620f0a54561cf66598842fca7b5bf412ce362692d55f10151773ffff3a0147ac4b9fa29bab2ea50c05f56f40bd35fbdeae573c4b8a06545df8c530648fd4f2b101ae5363fe861924e1abb1a3843528fa6169e3e21a73707839b32c7a76b20d6f4e7370da46908701ddcbc8bfd4c2916c34bf9bfa4b70d18a49f69d86980fd9cec005f33cde88e5741f3b4e1f18fa749182461e7446464c0b4459eecc4c047af023030e3182d5110647240ea500f4adf0a0ae28a789faa834e68dad266220f44bd455e680633a5b3c0ea3527c039498031d711746e25d56d422eaf0ee222e0f2452d6c715152a41734c9e517c328f69ecb1713138b5f48cdc9b90a04abb16a39c4c028989d6818cf52c332832ac290b51ca0306e0917dbf8bf08b9f9c48a064658c05e28fb621bab17218fbd965585b05db5ae9e6e164da8189f9480b8056fc79df4e6eebf4f08378df9f8d989ddd530f587f9d1784fce35512eb31240cbbb5beb96c812e8a888ae84753d4b81fdeeb77f6bffab532925d4fea9217d20779d6d9470871f2568ce22b143e56cc6ed53899bff2e3723ec56e552036f2f06179e2a0e666be3b51eab52c4f1af3ee9b80644f9fa161561ca70b161281ecfd63772cec669e6adf0200272fe279dea0a0c6832836a6ac4d1426dda952ed8a30f712f8b8172dbe4a24c2f8116a9e12c27fa1845b76d8293b5bb2b36cea31d43e17bfaedf41cd26d195626d87c177f606889c5435eaa4c09a2cabacae1598e34a78898aa7252661b035728a958d0aa2b2e04ce928c059b95f364a93813d5520b0925f47ef7e97070e65d6d696d581b2df836f7446ab9aa13594dfd0fca632bfc12565457dbdd9a5fb1414eff875b6da4180d55b3a53ea0fb0e68bc5d92b2101d14b578f545d63668d76aa061eb8dd3fa14c5fe47b08906aa0d2d005ed3f1031e5f0e47c633ff64e42cea59eab61cfcadd75547ec80cdf1ae1c0e0173e5ef0a34fb5f891945f68b6472dbc4d43597ec0c8496b309bdb7c27c024e2045c5694968cbcfcc247ced2dde806c655a29476a4dd576b5e21101dccbf015f23b6f015be66d4b6454e03111c318bf63ac23bfc83459dab25c40760113c0425deca2223403c032839b3ea93a77524406086ab661a6d2fcfdf661d6b79416daa0985c6479acc6c79f55a1ae1d754c9ce4d19ab376582d7a12ebe663ae0933924c3ce6f0a64f39e1b3d25005b13fb2e35b789845eac1be437a4c36add1e8919e3e965354de47b1eb3430c2039c09ef75b518e3c3c5b44c56c9ae129b781f8edf417ee27ca832553b37e4a4092eaad9f097501b8c99b77c42401f3a03fa5a2fd4632a69322ad4ba91c08412caceb46d0347fe8fb0a615bf965f78874dd88f3a366de96e99497713254e14bd277f03ddc5336717f7f6a5354c63464d5de7d5788c221e9929469a143ec478d7add7fc61278d31ed0e5fbe1c05e7970a31b9c2702a67eb31c1c354d2656b7d46153b9517fe81c9be0c6155e6f27e65cc0634c674d956e4d3e602c7df684413c983913a729239c21df272c639709c36c6e07f810499cc7c7c9831049154993478c9f69396a65c9dbe3e4a411b12a40e2c627e566950d3765b34a7a6c3ecd84df47f6274bbda998b3836cab5b1c2f57e32490d65a8fa9467a636ca76f7c95895135f18a323ce82639ead33a24d94adb6c4acf89c1b705dcece0964527298140f08a5c086b37979a470ae4b8a4e0b7dcc762b770b8067c153391dcb50c5596919adadf7ec0e06ba2519efafe71422e322eea71937c8ff578a04e4a308aaae4a95e43b23dc21b97776807317b5023f7d71c611154323df6cb19d9127438a6bd2940975bcd2f97dbf0c8b583317b42df85f879135455b86c0288a95257c1ff4ef22a1538b0d452155f4d2f66094f94af753a78ac5c5d8041b40b6e6341815e8ba048b8a11ec84ef4f892bdcdaeda6df044a5cbeae18d4cb769ff88f6b68e4b580fd2ec3d1e4e57852e680f91121bbad9b31bd56eabd68a437428cb8b8dc86892788abc65a17820e1d2354ef14128a8acc112c3beca2845f92a446e849848cd88f0bda35d54a43c36f90e74092d1b7a98d371fc80942df8f43c5775d884fca8cb17e3906c27d81fcd2cf31be623e0784f3fd5ae3f2079d647b59409dd09ac82e5c10055cc012992c6a539d58ac8da262260204a0f4be8e30ce8dd2acd8a438a46e01ed2830ddff7b01348d737b075e9dc982b7a4fdee79227df78812c758655d22da89f836b6081a98ea1f234fe053cb580a04f03c66826a061da59aba1edfb0fc3b1cccbf4ef8472500d24bb7666e752a5020c36cea34e20df674b1bd9dd9da536b312a2c06454ddd490dc8160099b15b9661ae156057091fb3286bfcdc2bc72325d96dec138cf1262fa68e9a2649e0f00ccb9fd0f7aa302525770d7bf6e4c292b228e6cdebd311d968340dfd25dbe87632edd72ed689158563837ca3657351ba68f4b3a6bb8c96034fa615f00379ee793b522a851d19ab1d30eb836cc6141aba7666772a73ffa2951718bf4cb3a8d427693d35f36d1a1224434eaf461dcd99f0fd4e58b798f49a4beab2e825c55dc3e121c247665e486ea4c8cc24455949a20ccd5168b7936b0213c5cc67fffbff93eb967fb499eddabf0aca8c058fcad34e7e1c00ef08c0ebc0d5a305e661599747b0bcfbf400b92d74f47bea4560d6552bf7d3399450eee862c4d025991b388a2b22938166d597887362919451b01b46c90e0110ae542e30e13a7ba3256d384b2fadb034b06deb7439d7e1b6af08c419484458a8d21421aca3946a28bee6fda75f769a00e06a7d1ec9ec3e18da795d39b12515a9f0f34c38d7cb8d357db84c37e11e29aeaeed308546799e73cd1467bb89cf986144802f0d7713ccca2ac3398b2568e446cf03f90ee76a9d295c66d639bf33173605ee99a6d5724a2a7ea0c9b4d3f83789b938ec9fbb49b736dca311ac33477a732ff721070bf60b64a09a64baaaa86856db651ce8ebd2a4a271016a1223ab7d336ac19f45c90ad1928308aa083037c20fdfd11509e444c17a2f317ffbf188c8dc15d22cf6594d0dfa41ea79910710d326205619814ed6500eb36159a8d6407f6acd4c46395fc6d5e1cd3781f5ed8792dbaa785ccd3d7e4c78799672dec1e92753fcabcc8c4c915612bc865a8963cf8055a2f9a83f14566efd46289a11b59c65ec0ab151cc22fa4f3e6b6d1ae94b868910968ae0e91b27744e164a0b1f9603ec111d66698bbb52af30fa7fefe77feecf3595618fe36c3aadc82d687682346e7f54c9e06fa6eba404560aafde5fec68b6b33c65e01ca448e3e285688b1b8cdcb1f72af2df37cb993ee7b111cc207b2413f0933bbf799ec3d616b96785749d812b9990f1be31c24a039f759e6ec587d94be316d391a893adb7f13ec2ec51657bb7637c87e7a027b2ef9575c0352a4aa6ee5ba07280cb420043b024017f4ca52296d0aa8125adac1e4ec52be6063fe28caf8663b398a83bcb1df012eb3ae24035736f7233babbdf1e21d8bb0a65707a5d189d64e7a44d3e89b47fe4ea5d762752da6bdca9c10fe01f5bd8e13b346350d44412beffab0809f834eb8ac19bda77efc945619f332d34884356dafc2aae76a43646b0982917b13340ad8e86d14d5e2e429bb02082979cb4adfc422a347d7219d87f5837166d26fc47cda84a5731193719d95f9f22cb31e6d67cab3e886b974b48b5f2a1d79f6721e22b87539fab0b4cd4f44042d154215fa0044d3ad7701b6d61e88530afe5e6dc4abe53cc85d2aa828778db04ae4974e34dd1b0c9ba852b17a8e9116adb0a740a19f18b4ec6a7a37c7789ec35abb3aca8d2b03597cc5d8475541e0f361be0518450c66f063847344357a6ab75eac9e5a7f48f8a1d0e540b0e43700da766ccd6a8be2b93310f132e127f0d4631e39cade8b11a766fd0f7ad9b3ccc1cf7156f7eb41ac35a7746761b1ba31003a31b679a1c28872ad17ab1460a6f0547010819f8185bf8c3d17aec85d50ac53e4657e53148b282aa3cc4949add3d4cb98a41d8413280659209276a943eecf38ad20ee94ce91981d2d5cbfa610e11aeaaee243f790f88f305a4f655be99b9aefefe3a13d73781a347edced7d77ff160fafeebc30ef1670e98264eacabdd193392ad3d3663a4e917dd08ed5561576ba17392b085332e2a14b8bbd321a0851005eb4c4aa53722e252b46761edf7748dd5a26db36af43977b2fe48ec79379fe4a1f203359a95bf2d1feec99d404037627393f0c417d31a90838880ce4f76127f4775152d6c1cdbf738424dc7e4137b866077a28da4a545074a9e583a720e451eea2b307182123051b2df3e906f8255782cd978d181ec8932358edebcbf93c1f6ff7f08cebc8413f501ed92c61261066b813a971fd042962dfd668125a08b08f87260353008747146f8412fedf659489f0ab903a3cb15155e108a2209838594c7c2d5aa0d7677752fd125981640482d4c15b7b6f0c01974b20cc1b6737b57ed2bb0eade5e2f9f5d7d579b19ac8e8d706ba74929278aa3d3177f120c98cf90da23db0b9ebb657f199908dde9578048090a8be66202b77570a664bf277afa6e2af23f2a5159b962317af8e5dceb33b08e717b77a1e9e7ca02cfc5a9eb5572620324de165004d50e12d180ac76974f9a7f1b59c4b9247b1bfa977d389f3b8109587612932de21e637b6098d6fe9d81afe70d82a89b3638c190ea9277dab218e4b102d8f96ee115eae983f2ce8b491450b6227f558fbc3a631cccf313be51efd9883a58e616355977dcacd7318630fb268df48eff639e65c3412050e3c95f092b0bb810b7187901ebaf54fcf253f7512e562c19249d1fc764c7a38888d818f7655104534906d426fe92f0c0b3167c2329d58e6e57c3e7d8bf9ab5720fa1541c53261994ec9ba1f3d5ee2a6bd0101aeaef8cbe58413c9b4474664250897ff09a8ec3489f079ef7d22eb24b6e7ce5c2d26a75efd4852d9f9cde25aa3a977b7d7fc677724bef060a0157d125654dccd11e743f725f8f7f37af13d79c327c2842ddde284670841a6cb12a477bad93f7abd9ecd9656fb3ad268df4cb3e62852040dcb869d4ce4ff5aef61de245102701563a94feff5f189c5b73f077eedf5ba5ad30421cc088cd28b1c293d20ae69429f6f447e254bb34010c29c7ed5b3047baaeaded58306697569d1a390b2ba5d086e0438d063f110279f0c243ccc2e64a09f6fb8564b8fcebd130ef2c7317e22bef5c4043aaf7986d01dbd0c4c264fbfa6262dbec7074fc06ff339e59f5a8ed186574e08a5fdc968f082c59c63f07e5c206fb50172fae399f111b3ad64850f274ddb2a7cf35c24276b8c5ac171a62b24d39487457e12f68c5ccc3ba88208103591286f92021926710796f85f8d7220f983ed9335272a79e116d6f3a25adad0fe83f2d4541827e759ac1387d437c84568ff0df8b6dbe3546048466a683450fd48f8ea3f9e19270f368195f8da1f14b6d9efc3350ab3b0cf9d60e78008c8dcfcc8b4e31d0700db9490e2a51f0fea43e8f15316e8c61f6428b4c816aff00a344d7694fb498fe607e7db414364a4693dfc33e473a26be4b5e943cdd445a3626ac65ffd4db885d9dd586cb79670af9204231251a0498fbb01d086d5c62720da18f1e7ebcfc5d6c9da41f3525fc1d6b16b7245a6d41a4ca1007cd3f6cfcd18e826817404b65a5176a3224681e8ea28b9a06c343b9695bf867e33d4bed31df39309d813dac34d8c86c0dcc3f1ee5993cb0733806ff9b253149d0e39bd54d48e71b2a1631e84359cf47e1d805ce0683440f40fc2fa7a2a5f2ef0a8e53e15fd1eace220e6110236c9ddd92a0fa7a7a28ecfb742d6e490037507691d8ad494830abd46181234fb780799a14e6990604f51da957e876536cf2511ae4e832e94424e0c63311fc2c2a0242522a5a13712a8f694f858bf9388015048ef15c0a7096d446340616319bab5410283ecdbc5ba3e641d9680a718d561cb03846cb4c029ac9dfce63c619153d153084a6381c8c3bd8fc1ba5e235090a91215ac5698ad793baf84a8dede2a1ea411e4d4ecccafafeed5414001260298b03c7b684cabe461a3f27e7e17e569412411c0f6b5a17f1de97752d890f6996c8cd38dcea2a7332581cfb03750470661c983b1855cf60a2f6a055a1069ab87a1ff9bd4b8e109f688d1446e853050e0de79049b9e75e281368e012fac46f176b2593e3535d8b20cd709343abdd4c0cd7b7158bf2a8028f303c75efa77e6fa59233f1cbb6b7b35ff43f19a41a81864f722e3c7b9c0c36791965cac25e39b08b60b614462a136cfd51f5bd5c82072581cba62b88804e661a922e818092e1d023bfee199fe9b612ed94052a5ab0f79bf9b28e0790c4562ff97d3ec93896f1365ace1097f88fb89eb32786c8638b4d7788f72ea56bf53e4c5273bd251b77cf26eefeccd3c8491df5a88f273ab24a59e377025703f7f9a2e9eab76ed79266dc5b335d1eda15931cd977a3ae0872858727013eee5e5d152fef0261b576e2c3d9c332428436d0d9690d3c2dad5a0777b980d9161687fd0a3c6074d23f9a214892a3a681e46846b34dcfa9571c20afc0a0be85be147903e9fd511e371d59ee33c9bbdd994fbbbea19d0fb6bee7154c3b3880fb8525dcaa4e68ff8de586897dc1f4dbaa2cdbdc4658a7fc39d4d6094141cdb95d46df148b2d61ac0235ff98ffefdaba6ecdddd9e40e91f32640b98a8b741ca07b4bc39c871f53ed5deaca187ca06ab0913dc6c5fefbd05888ab460aaf5f0809d46165306509cd44e91bb0d0cb38859918d4e357959c50dbe3a2c6f4cb900374515c066bf32cb4e48ead266d878a35243bf3a72450e7fedd402fd6def8db1bcfb4c019545a056e364d3c3043010b1240711054d07078be130f18b905ab770e52fecee3963ac23d3cf83b1ddcd2dc60b2c97c13d2489bf538b49cd9b8f9ff715c5a9727c0667c615497747b14b32c9d9cb71eeaddecede17d49bd1c7bb5b40141b5c70a72471f435bd99cf02b72542942800a383a88566665c4450376b623b7e4f0cea0251411a6345ee95d6bdf17a792324dd7b30b9253a5cd89f1c2204d6b8eed8c8ff8870431dca170accf578756fedecde447c869fe180207ee7e93533e00e319e9379f81538159c6e16c8b13d17a3aa11c3a2d45613e27d7a3d310f5900fc0a49863747966f76c682ffcac10d2c19b5476811c235c720949922dded57904ac3ec1a0da345bc34b70edd51043adf10d355996b999a135b19374a8142ae3194898910495d8c66b28ce41ee119f233124c9fbdf1fff645e72da946e6c6e32b9fc008cb2adbf60b088c507ebe1905b26a6d5c0a21f4795f1c11d485f47315e8c32ed23caedaec641bc80c5cf1d602b416f79d4ed4b30b6ce47327f00465ba755dd964b38675be7730a4efa1dc1a3bd30eb5556b8ed464c1812b41dd8680c13ba2255a8ab6b7232377e52d651bd29a9247bdeb6297baf9f922efeb85638cd865b42418375491e89c9871c1649a97559ceee51270f47e505349051a0b857656563b1599c96a2b4252e451c3a128fc9009bd98ca2da942bf5362a7878503bd9fdc0306040815efd2c20961aba617d77839c97b44aa68c25aae4cc576ef46ce5d41e166550ddb0b4f1025e929b31f580b7e91745617142be56f9fdb024c4cb51f5bf31c19003a08d7bfd140cdf5a95a2584d39d54bda1ac859f2a2a3a389b5b5d8c81252bea355b6cf0df659c63b0a473b30bf21dcd8feac90897050e38179c09613c3266e3b0e2dffffa737bbb8bb7f8f4044c6e6ecc1f61945f6ebff4e0f75a00b49d6fd0c306e0f07b0354e71b8039c81cb40d59df5e37df585f9e48a89d84a41f277d3e53b4d831afb2b6fde24d701910cbbe8190eb7bc03430a30fd9cafbaf8efb8c033e313bf6a7ba2646ebec68b1ef915390c6609e8d126e9fa63df6c9d5b9037f6c27c8ec937209a24ad248230a5bcfdc37993a91eb195b624c10477c1115d5748b2fe3e845bfb4d771b132f628cfbb692d96f3f1e4736d83bdfd18b368e8bd06500ae37dbc46d3372ce3036001a235ccdfc3ec18375d82bbcdb54ee0016ce9c8ccf0d515e687ad4bdc8d004e85bf2ceb2651338f6aeeafa17a9180ef50d309eb6238fcc932d93156a71d6c59615a7bd8015036604d4102528dcd47165af18fe912d9e78fdccde1b706bf77f18a7c531d4cdd6c2ae67d368eb22f4620e8645a23f640d8464ccb8724a0a7f703dec4b523d0f985d50640bf12c4bf44628c5e3eb692966558cffe7cbf8f6b37dea632a203ac77b65806128e462e0e8d65790268401fc28303e5ee0df638c458ce1bac7ed5e49ccc928554ad204b5f1941dc91faba9d0638ef9c0a5e479ad0f5f4f2cec36b5e78586399b4aec8336ffdc3286abaccf4e41e74dca30b1d8cb489e59cb594766b6587246d4b75a8451d5c632fd525b364f3ea18754298d6823e2258e9e3069adb401058b2fe06d592c82d140f924c1ee79b2d787710c8372f8b7e7ac19fb7ac02aecfcd10cd89fe20ae34ae2a6a5726dac0220e57505526abc3fcdee5e45ec2a490ef3d88e45ad2b1b12203297c90cd6ef0c917c6090f2bfc6f92f223e358bcc459cecc2e3fc5ea95ecc7a34e6127a8e9642d2deb7e63bb43e15473128352f6d5cb6f9ac1570602d5c65a845a75124880420797cefc38f53fe68de0786c08fe0ad885235d55136dd2f98473e41d4faf173145562e5f293f3cb01ca354252af9b127da29d58dbe5f939a40784b11869e9bcb217ba06ae5182f0c9004e92884b771a3cb1a3bf4b4f04127042f58e53bb166da9f9b9f83200224b7db4744cb7de4dc76adb847198765fbc565f49db706a367f66d24bec43583f2069abd00c920f726314b444f5367a35a70fd1cd07a7277507d59be879bde5cba4d70ae6db73bc43eaddfa10ad9983b92ef25a118847c97f3e666ad5f1788fe8912c0ced141583af53898f7cfe31f879a6847a2b197a2ec686e67b5e594b3e7249581e66472952de542d3480bfab0a8cd91f92354dd50bff9e9cddbb586dce34d72ec6af3a006e8f5e5ead17fff83fcb8278f12fe35caa6eeedc88da02c1f5b8286dae745859a03a7e2c1f994755731b95c88595cf66a8b1800b3385f7be678c0670ddf344d502be3376db6bc8d8e6244cdbc18e177ed457b0a5ca94601e344a60389c4efe942b909c9e8b27993be02d2873322752bda6883883dabe1698a8d7c4d24f9171967e0de5ebbf198a585822e1b5793e4bd09b932936a95c102930d76c3561eb54f0ba52c0d346ce7293a6d5ad8260045e4a2bde26dd98af5a89526c33478ecf6ef0a0af41f4b72bb9495308e279df66274882fc602bf36c6559b51516463f8faf65db9b93ae92f665fe0c0e2b2efa43835bc9fc2d07d04515aa7f2b4da73ac02f0725bb08cb61e2cccac97de7078dd92acdee9bf0044be254612af2986201db9fe57d3038ac7917eaf17d493c521d4becbb5bd1f899fd036302e97afc34ace33696ce54d0a443847aa0c615ddebd03d641f1295502f2eb0f51d02be496fef003eae7627693da172fb22553afa83886adc7d878bb43146e8accbf98a7b86d41604ce015cd8216eb64ab99950a1dbd9fb16e6df291bfa367ac9c1a581fb25f0b5770efd2e3ac765ef6cdae6edacf7906c5bc8fabfafe98663e54ed91871c56c0c1fb84085fea903dc51cac53099a20da017ecd8ade4b0ecb7119c013a92ccfa5537d63866601335f11487c06531a2d3b071369b9f3ebbb97ed81281f2d4d6f8d79bbfe0e604ec8c71e273f9bc61024ec5c4efca655c5e65ec293d15360130c5b3c1f904c1be1996c21630b41064680adab6a13a4f2692e3fdc5ac7be1b19683ea999593ae4b20053c7be1e73ed634b487c71cf9def3d0983f8847723a7f4a2376f48d99882ffe98b0efc94718f75cf15ff46ffb0f8c7bfa5fdc8ba3e0ef7255f9d77ef53ea87aa3c07ded59452d678f142d62849455528a1e557b87b4dcd5265a997375766b7d32e3f1084bd158e05371f0d9c6f57bb53e94b0fc431dc9a23f20cbd76a6d9289fa0085bd00e4eec236a4d70ec1239b40023fab4d087b40974801336a4806edfc4be441fabc65059f5bc508e030b60408aaaed49f762f82d0e048f101a96a4bcb8dcdfba0fc7da832872bee908a202d7cad98e57bc8c6a10750042e5296bf1e948a6f30866feefac259a010ebb64675caf973ab441e80a73ccbfb8b257bed6357efc34863502e72ac88a136ee4e4d2276dc2886bf0c7dbebf8939c3efcae970125c5c2a69586cbeb497674232dbeb74302df81bdc33e8c4e0c5cb58ee55f57c5c4306df8e6f168a18e4c9f70095b875d0f8f07127d4915ef969f0cc45093e9ead97d5b8f318f3a86364179fcb8084f9a4e7d9451f64f3d8c268df2c9db71f822cd311f6b5c7e40dc924cf47ae43a871c8147390af825183753476200b3a68b1a55d5ff6a258fb901f7ebbe48a9564a1beb556b0527377545e452f05d3a4719337210d485453e313bb1fb85532c3d69b6f3b717274b733f615c326a91adf134ee83d3899fc0cd9873141a2259a3f6ea267a3c98c1c634f424657126f02389c195ec8064518e237d34d2648382e22ad628c8c1652569b28b452b6f5583dd6fe18bdc0d5ae942acaf3f57686ebc4f0cdec8d94212b47b493bcf9a348676ec5f54ed450bcc8951d6f52cf6bac550b253225c723a506a2af7e1e924ca6a88b52f3ced339b5434d7d28ff0d210ba39cbd1b254387360056fbb7aacc3f4b957ba8ff2aae43033fc4cf4a86d2e08ade3e72bce30dbea7dce2b94e534eadf506453a346411d138502819ea3e746e1621e516fb8cd25c573e53999bdcb4f1dbbcedff3407217b46629f90034a94ea329b00bcf6e40cba52d38f5b3adcb5589b4ccc0683c48bd1c9556a6480889c0df5fb711084f9625badac7e3bca85bd977908eb838e4f97ef653631f3f6996383b35f2637bb52f7d058b09fce49287880e33323619f9d6d1d7feda8feb931a7be44952655569f3b88d7a3e160d2fa2fce050d02b64ad696282849db6c27ed9348c32783671a61ca207d23bcf60ebb83d96cb9da3f8ff553804b78a03856358c1118cfd6ab9b78b194583ef1680b93a9efc5ce47678b76329a48e4a0e64ae59dbdc15dc46ecd3ecd61f617e6b4f5da9a4c61edadf4d97b42e24538d0c983aa8aff18f260baee2afd9a81a8ea45c2d5f14813350da1a48500be5075459cebb9e3f5fd6b974107eb28fc2db2538befd399a72c3d0735dd1e5156e0ec35d87a4d8cbf710648320d8ef5d2c4782a1ba3fae8996112e14588612b191d808b355daf63f75c71cb91f3539f93a927cd5848c850d9d4557aa9a56933b27d3df0c9bd6d166ac4ed7524de5dc486f7758981484864c670c4d19813c30f92bc92dcc5cf69da2f2c362b45652ef2fc85e131949a0b4c53474a33bacf366799972f5555e637a20618dd31942ae49c25d9484044cad9cfdaec9482bd90f708bce7f5af94322b797f650d8562f29baa1d03d1c51432bc0eaf2e3855aca169ef88cb7a1ec884a7cf73328755cae34d8d3d8cab9af479e05816d22f6e15378636777a623782d6d7e741e6e55a8f2f68b3b9e725a0b4582606e42404033afc59665b30ce3a4e078eafd1f4e20db077905d7bc15870ea627c38073018d0f8c5d7f02943a357fee303585165b70a8699c701846511aaa8cf5f08723dd9790e3e43b5482d0c33fe3b5bcc8409f37593f3f8c82e8c4524b4a4c9bd654a29a53a0649064606de7ed84cf6a879fa7502d5f4a1e297a642f069829631fb53d571663977666b9467c33778a9196645c020248c95a20820ad8f3203a0c609c3512c1c45e9a83e4a9919e23818a82527d7d7c971d1f4292723ff8d945c00869897189a8c729a122ea0215a62c90c7796209aa1ca8cb0482ea26cac4cfe4184d5f79cc3ef620e8c78ca8aaf31baff70b7c2a50d9b4509a4c933255cb0436f41f3e40d574f144fa228c28443ff302983a589218e0042e9889ca12a8e303ac304104934af76c34063c4ee1845cc4619238b7245942b64b807b332b332333333333393607af9b1f2327f6c6666de6233b3fc3ef5546777d89e50ead1afad4a43a0f43c958ffed8d74f9a5e4ddfcfcb3ff49a6ba9e4babb8b7c1e161ca8aa2e89efe2f869772a95943ee2eca14f4ce9eef28cb75f014f7a5a785edb6c909ce4a69801f3813ba6cc7861abd52def806b632eb77ce8b697439fbebfdcbc7d32dcfdd4bd06e69e820a199ddcdfee4b16443bc50cd844314b66b14174fcbc922b6ecb97a24e71961f86eeff9bc7f4aafa7deb5938b406bf9e366e333927653fb70a3d30fdb755b89aed227cd8a35133f2784a609119553376349c264966b833a38d195fdb999fe651682af86ec7f5dd5d5f8f71e3478fd1636fecd8bed2c9dd01b338cbff7effda10a1fe03eb552cef2ba3982573f0b8a7e3293c0e6c249127824e9b276e5135a75ff102727a0abc3bbfb316646a3b0731fab27698e5fbb5ba7fc1ee6ef66fed80dcb10572074e6bb5b7e2171f82ceb81f7fe3c63d013e17b7ea11e2ed731faa8cf3ef718ee338cedd754c67771b489ee72fa5175d6b2e8606565801b21c7f77e32faf769a99f9979999997797797b37faeeeceeeeee2279eb5dd920864d85ad836d17b37afe06b37698d5df4380edc31efefdd8527a9eeced5788f753a382b0d399675f9f8dafc5ac1d66de222fd49a0e5909238852aa542e55693375763466ba9315c1613014b007fb3e54d301aad3cb0df04d67af50101ba015e8193b254234c1cebd1121f2870241a666dca892147777573aa590c2c4d01cc0d4191344934a95339383a954aad0d03738bcd58d3fcafd6bbe1f3ff8e753f3a8efc78dffd7fc0fd50fef7f5edff2ef041fd4d37c06b62ff4667cd88a2f7d69f0db4d7c1c9db3c5ef840ce8abe7fbe1da52df8df7c13678ef6bbe1f4ab0c4e697d4bcbe6ee2870328f53fe0407d3ff6950a5586c3e6bb89bfaf1b9f9a477dcd37bbc264fe10bf533836f87cbc47edcbfb7aa873b6ee51f05e5f5bcddb3c091bbcf7d978bfc177c31b7ccd77c3fa3a6d6fd34428c8cd5b6db04261c65c4ee100d2e0bbd9aff99fd70cef7fc6f635ab9bfd19fff3f2563533b6f7bcaf791fef6b7ebfa6e6677c35dfc23a67f3f4558342e76c341fea5b6ad9c6271cba13f8b7476ba8667c119467a539f16334bd7cc2a362d56589a53c31c6efdbc2f0509f7aaefb6efaf4ddf406cdf8a7efcfa7836db73aad946e74a9eb5ebfeb6a383a27fe0fedf8a74f3bfecfeba7ebe99ce885a0f30495eaf0cb37e977dd4a519f7ef2c6e793e0549976ab73f49788392af16caf385475d402430e705a9da33c5e0efb38e3b9196eb55a2dcff3bcb8308d022727898981cc10365b730a313e2c112609333078d54802c344920d2b7839f21a622808194b2d7831c62bd4a4d50bb0d8820b9a16658abc42558a22c403a24499fcae28474c7e1d51585367515c882d219149dd698144a7580f342bd4021bd5312436184c2d185918904c8632c68c536750be6042f50a983a8b3901254c11474120f6c5cc8ad0544d9d4181a29d74bcdcbd2736b564e6a67affb645ce659bf8b49d3ab3e4b60f398ee3fc57aacacea9d26d40b82c6d4484188820420991222d881041134194d0665044148bc11613b39551a626463ba1b4e898518c092b518074b12a9c8c900931888ca124082c33b430c61625649009b962c916ab22bb993a8be960e4492765f0f495eb3cd51f40558374725757d649c9a92e141dd7f9778e02610997d3df2b5f5e11e2c6fdb8bcb573fb3e15b2221fcfbf7526448c0fbe10b489f2b9faa90656d071da3cf410790022b5809c2da725840b1b4c9d0501461899d49d16d8a1fc3162072657805430324394195c40c4080e2ac4800c2551a4a630a9156049f5414411c2bcbbfeb9cc0276eead978d5a761491f6ee7ed9fe71ef62d487be6df3f6f676f4ee2e3c976204eff11bae59a7ba3552355da678a2040b49c8f0727777153ca9fa810799cddc8457e164d105d4141936de3280081c154d014c9d1dd98042c2042164a240d2444da80718b43842862ebc88610966010a20dc7d43b5cffe76f096047be8792e515dd4aa5249e92c39fee114969bd6e558211383284b7c580244881194ae005e247183175524014ac225dbe3c66159f9791fab0532b165180da5bc407993aaff868de9e3b62f94cd41f3a0bec40a5e8ec0200828cd3dcd2b2cd97ea07ca1b2831832c25ca1c54ba5a250dec4617520830224012a31d5f4a9b32851fe8ab92bb5b066c8411243cc7e6f59d06c19972944cc7e1640ec2539266560aa52c6e4eeeebe3a49b81195769738488099ec523ac509e3226d155203d333731cd71d777bf7dbca8414669bb0b3e317026ae6501d7f6ed54d7ad544ab3eb2ea24abd661d54c981537b61fc4af83d68322b4467fed82eaf817a90e1940bcfe50b23a2749755c7ea122c542d6dc98bf94ae72d4b74b9de32f93f0f8bd5b4a29bb25f77510b3fc072feca00ee2a11b6596bb7750fc422f28c24e45f098bbc178d820d59270b09452daf4514b73501dcc7d96d20665c3010eaadb66abd2edaa7631c6185523b37f6e12c20bbd25bc10b511f1f4435f2db6bffd685df3c0201d1f14d43cabb439fb527abb39bb9f8e5aad3614b26c4a98106f3fe8242e8ceab40c2677d27e1063eb4118ad118b509d6f62861c910c3622a2e6d97cd09cc924e48ed866449b11441b471465553624b90347596c3fd8a4500f8ad01a9b962caa33032fdc88b62c527a9e4a15c631948787e62875a10b86d2f00f4365e64b99c948783bf53f3a95192e90131d25306b938ef8a5748444d0dc973f207164ee3baa029de26c4e78fa5db7993a67b9d58d0fe775ce2acd8c9b09e84c08d00d0992bb21e19482ed7d30f3ef6a477f37227893df9bfcdf0e9e3bfa752604a867af74b682d1e495093553ffb4ba21019582d3a35e5f727543c2b6bdbe3495a37beebbf1e14ccf6d1fc7729c4ebf7d373e9bfc0df571d1af4e384cdc8bd32293352ff713392a911e8cc8362c16a94e0c22ca88c428937f873a8753732b8a43bee44bbe1483e450f3c82336e8075c0cba2b6e0889f27033680e7fabc500062000013737211714c6a1c935f6fdc3335ec8d5c2a529d520a09d1372b1c9cf6d41d25371352e88d641b40ea2c98f6b19bc90ab0d79617462f2378d6863d23ca7df8a267fec4279b89a92d6e0423ff0e78f34e5e1649de384d6881f957e9be1854d85af36862ac90b2352d3263b6af61695681bcb688dfd160adfcae0a095ac79b658e7d49a67a3d01c7e95caf3b698cc0af9c593c1e4af7517b0c94b2b4cfefe18b65a9aea3ca11ef06f6378ddb06d43dab6188bf58fd41bd2e42d166eb149d4395b34c387bd0e9e0d4725977f1f344356546a1e19957aa392fc56a973aa7861c3268c8324a4cc53d2290e0eb3bbbbab39b2c290503c9e373e34b299faeec6878663dfcd8d0fcd8f07247571f4a2f99f174a0c8f08af6bc0e9d5bf5553add6c9b7308dfe3ad87ef03a38799b122ff4e6f2129b114389fbc44acca3605e9d691582ce1b7fd3fb83995cdb695a25cddda2046b98eef602e17df3b4784c0799b5bc994e27f9fc853c6c5860166f873f909f4f271d559dad79fcc528295262528a886c4c9da95bcaefa4945232cb6effbe0d5a9811678c30f35da444cc48890eb317d69a49599a49519abb2e783a3342ca0c3be8cceca02c7387ba08f3e86bbf5369e4857c867964a7c4782fb4d0646e900e1e43307da66d5b8ee3ef1a255334453cd32bb324b35221a8b24cdfb9e94350d5519dfe14189efc427e666fb53066f9fbf319a6c1df06f9b7b940ce3cd46f42781d333333f7bd2ac12ecd1dda5f9842d00468660751f198767a6dd31106fa95fa4eabd0337d21d3fc512b1b3e9875c4d20c17b6bf413b3b4269ee2f10d3d80e326177697542089d6935816eee347d21bfa726c093c3ede3b14b33f66096a2a69a8678e1cfcfd426aa2dcd9d9ec72a4f882755eaa17c9fffe6e3f409e389621afb350b1d17c413e3f1870b34f7e3108f1f68f6d1d4a5d75253f1720b4ee89dface25129e8e129886fcd4eff74b5355dda243be4cfd0fe8072b745381b8e38fb406f7dc4cbe66225bf6c6f6032ec2dfa55634dca186f828b61fb0d17ac0cf7d91dd986649cd6b8a847dd57c3f5278d17c3ed4479ac7635f34554c261f21da32c37e72d445dda49918ed07cd837ac0dfdc17f28c4c02bfc56ddb7efb2e6ebef9e6d13d0153675e92701a8785c7da69812d1c504addd60ed8023bc0697919a2456b327ae38fd3fdea20f3ebe7e20bb03333abebc66aabab476d304b470923e0d15daccc8029c52960a0a8ddd0b4968137838ba7438fb9e9d7c9adfa7cc3d5b3d3d3e35ad78d1e578fbeb7cbbbcba568f2de50e3c678de02c0d4d9172e6613a1b55a2d187451ea619596f3d28af1377a8cae8ea06b47c7084ae0a1394010e492c36c7fc1ee91f6f3f26c6c73faf553f3b71e00a0357cf65a79a277ca6c9f1da3a494cc82924cf2c69d735f78f17d726ccf993825bf53209cc0bdecb6bfe9bad36f8c791688a68bc434c0300d9d39015bd997d9354166864b9b494d64396d46d3f4859e4f8e0e30abbbf0e2ef08560ab3e27bdc96c5fcb70466b5bbbbf74a89bfb16ddf36c62be3d1c3070a3f36a631c6dabfa12e0b7ec559f582ab50df9fbf1d66ad909df92c667debebeb36f477c0756e5edddddd7d9f5ddedd5a0c26ef9676ff9900781b1ad469d3a0260003f819affa425d6256003e007c1bac94591a7c0358a992f729b36abe546b92b700583ed9d4e4be681ee979aa248e374868490a2d8c9f03dae6064d304eb00bcf7f7f8fd8005d202b764b176803046db840d7265922b0b709e82008d69c80128b9d1dcee1df1dce0b6fbfbf0370c7c60ecb06478ed2c60eb8b303dad8b1d1da222fdcd901431d0a358bcfce4e7fa1b733f4846c4c933140354e1cb17cee86009e65f6eb4c8c987902d50c5180c160d387aa8646bb5347ce7f539d7ed9fc85feedf0b80aa5e73bfc7deef047f94b544a755487df5735e14f3373038125c9d4591340f3cad4199626d38603adb1ddccfd01a89cea4d45cdd4d4191630a6be095bc63ca14f26e993fb4202d018f17aeaf47eeef99317e06f47bfcf1dfd2bc92c548aa646756ca48dcdbb50277bcad40fbb57367774bbbbafaa7faaca22a011874eb506aa533da99a989d598cf2d9ddccccccccfaaad151ccd2e7f7b899796b7a4d474c5218c1c1880d595081a46408045cb6b832648b2f62491cd5e84e0b7421255443374548c96a8962f26ba125cb64d56b99699152a52b426405329058410c4b78b88267983b24280c2d5a48218225044a445bbe1c01450c517e8a0451e5cc1057948a8678b2ca83d35f8f243e727ca19e611e0f0c8f0fced95df56056ffe8691e168bd5a37f308fbe546992f063c314bdbcaf791f3b37702399d55ee7f4d7fc4e5e997a684ebf942c2e4b0f13fad5c51d4df4ff998b9aa8a7a1f1e5ee69563f9353dda356a1d2e29f56f26b5632dca510baa9aab3497fde1eabd9af7a14cb1e4c36dbfd4900b1d70f01c45edd9f7e7f3f6596890c4f676823b92996e38c083093357546440b3322cc4c9d1161a4337536c496e99acad38a1d71b2049221a030f3e2ef168ce97fa65c851235230a3445bcd91059e6f69aeae7563f7357eb653de88f2b1ef9d29af37045894cc8eb76212ffc03d8601a070061dbfc99f367ff4ee098ddddb7edd99fa3afa7ea9a2c9aedeedebd8b89a62f698deefd370ce5612296a9720cdcc27406327d93fcebbe2da8737a1bfaa45a3f0164492abf50b54597451d229348b405cdbec0cfcf9f6c31974958e84f667fb8c566cf7d12631a526451ac75cdccccccccccecdf83699c5edf07d390a657e9cc5a309bb43515b3351e62b6de1a0f1ac7a4539c7083cdfe0e36f71584a3e6598a489db38f8454ab2525cd0d3d68bf5e33cc6a25d569246f0f8a5fa431101007b1d00693d24ab385bc953c7ba473503e50f8c1344ccb437b645320c837994e434c23fce12132aad3ef2f78212f359925a611aed019eefb9b4c05b608b3868698d5df28e099e2140a75010e85e25228148a4b7d2751db721cc7ed72cb1e531faae3a48997c4f0f64d268e3b9d4c2613c79d4ca64e81204f26b90dc4671ac6ace62566b5d0120dfe22ad94f24183a9b32b433b665760539f66ea8c09a5b91f13350da6ce98c032f7a5b6c0b05873eebb7f61739538a973f6ab78fa6127cddd367fde50e00ac877b1c75a151d121eb30c41a08801f484155da89015209d124560f165871dc04061528617d24ce6c94a44608c8d5111112a43c64471c4c8c82b546929830603191c624ef00b2e1c59a181045ae022830d37d8c085052cb86881973196ae70a1cc4ba54b256a5687970009166a4e5821828a0bb2308ab2822d4060f1e115b6a6bbb730fd41a6c1414757808084b08031a5862745bc5cc6456cb4494e8a1022c50c4c48a102e51cb8008005c06c01c6136540914396fdf28200ec142b71bdf02d36b47ea4e010447694c50d4992a49216481d2a634cf162e40b2020385b9087a36a7d19a2923cdbe2284c17249ab062065fb0e00b0b14209274e4c5982266c4784922015348c085901b889200f182189197214e3856ec10458c93178c5061c6019522740872851723262bf0b28446052c1042a209231b5c68e2858619347c158a2e64a8a4d1fa001d29124409254cba501cb8ccf82105922fb8886185851c66560240c399ca6a04ba1c81ea82e4a3e1ec95010512228ac040055c6a7c439721291e522bac2c518114b417bc90e2318dcc01e249ea68518b81f72c77773fb98ca7edc49dfc4f5f6892ee2fffb5619e751864ad0a9434d2a9bd0015a778ac586a430c805d7a5716a5a5e80b9a114478e12449cd09f78415d9972a35a32048413ccff35add0307cac05203cc0c94326a4072e66647e6030f435d9a5899418d8d4bc517a002285a085333028733de3a65ba0a62307dfa7b3f2d4ca182183056aa7419f372777714bf78cf68a9f1800b5ca05551c3a1054236c804008218ac0419b970c4821035968abb0e9722cf69f8ceb68c0c5f74a1c488101848f808971c5248c22022b464849215b8dcddd91559949e281d9980b04207324964927448495b88d9420a4d0d9ac483142cd81206963062d032405a50c64950154c7ea8c10816f286ab2787edc90d618c10e28730336441c30a46765032b3a2451520220f1bd320934c83d0135487e016d1128697062d62b48c99fcb286d301ea0a154d8259ee1ab54d568724fc04146ab1cebc19313230aa59603258e7bcb647c4a0522b89557b24156d8f56a671a8c916a540fbb6490a59ba5f986b7b59a8b2ed43966edbb6794cc1cc69d5a3acf82e5ba2cd756532596cf66fac1c73a5a589ce9ea062eaec09dad4fe6aa669ca10789042688b22626216a131636aa80d625066edb4c0160eb8bcac606b77977db7b7777d7274a0b7bbbb3927ad562cb21e4362f67428d36d94604bef257f90a4c135428e129ac03257b7cc5531732517fe9ce4eeeeeeee762b3dedee2ed785d7afeb0bbac022dcccdedbdc64bbbb7b8b4e5e683710ff6effcf5bdd92fbcada79d75d77775d5557ddddddddfde284a322fe263d4fa5fa0d226ab2437b24049d5cd4a1355a6ac1c5e430267f27f4c7c8a489d752868a344f929973740e2b92ab03397420d9d0a1234707727440476bd41c3ad41d878fe740922d914e31c8079b675ddcf69a64fac0aef940d4eaf46083cda32fd951ad7b220e62a2adaaaaaaaaaaa715c8b1698b0adebef22af3f22a3333b38bf9468f770faf13712aa6b4240d6c6ddbb66ddbb66d1ebd057a07add60e333f97333dc8d74d375505419febad5bb55555db06e88a469d91c749e5e1dff7e714081e593bccda36dde2b66dbef9e6d17d83181c085800ac685c505599a3333737b3eb4673777733373383a014555555550541259f42bf5855555555558d1d25b7716ffaf8a13fc76d9f8dfdd0bf0ff4e736e9e29ce3388ee32217b9b8c5088e00894777ff0c272217dff869dcba6377c7eef6f6f6e8ee546843220f34582d486437d34022d386f88744b5acaaaaaaea17b52d825f6bc7adf0c216e86589aab3aaaaaaaaaa7e1932bbd552e2d1073944b9e8e23ef07d3bc203353e99e104ed5555551db681ae6ed76e6fd7eeee6eb0bb7b8bdddedeed6a6f7777eff6f68e3355dfef07b7d6eea838ccf2f676b05b3ba0ab6aecd8ed93a303ad19230ffdae57a87ab37ea08b41d60e5cebebd26503046db8bc41178320928d7ddee5ddbd993a030365f2824eb800d3c40b0d5e648d8b0cbc60c1454b4a02a000933faaf45e8ae4234847790f8ac562b1502edd25ca513898c5ee28e939cbfb9bec55ba5737768dd083438f6ce050a9542a1ccc1ae1a867041cde83e306b3f88893babb5bd53c3d472ad591ea0053673532478dc5179d8111d2195dd11a5c19305cb2987521a34dd8c9bce2b6d8907997977777777777797f07ec6e81bdb41dda1b60ea0ccc9096124bb6d757dcb8296a55dcf021d1954e71c3d5b3d3c3cddde362d78d1e578f8d9a0e590923e02173c9bc2e47cc665ec23932ca4536c3892b2bfd5b225f5fdc6f4bafc707470ea5ddc8c1fd9f03870fb39886c327078e1c23308b9dfdbf695f1b62f64783452fabb42028c1305effd66a50a5f8c297177446cd3d0e1939315d86780bbaa921a8af969441d293409ed4e1499494284f7a42b2c89124d275348f5429893628eeeab1396edbb66d019e06a8f44db904ad533325010002d3150000200c0805032291502cd04471fa14000f679642725230168823590ce5300c630c42840060080080184390324444ab00d0685dc3cd41d8143260c2820d76550f778f1ca6d2b2183d9ce5228f5b8f1168020508343a8ec0ba7c486dd163f13bf9cfd8f76a69fa95bb2c6715cf8e46233ffa0cc58851b9f6a791ec0aa80830ea2e275ba5d3476277e2cc83b2a4d1a70eff97514609a12061fd3386acb26c5789286459ad117ada29b3c719be2cd1a47b176516af6f0acf1419039065cd953de78fa67e612f6660bcdc42d830e95acf5ed373408e2e7be156aa0f2367cdd1699f655c11e47700b129e585ece600169a2830d385f14c8f1b4acfccb0321f5c4e94d56315308af78dea90ad31a16926fd41885d01fa62b1c9b106f22ed24ab38288f11f6a6181ec748385d492990664093b3a818c3068b27133f22d1689bdd16d38d8cc0637877fb8e464413009609522eab5b8a2f70e978c6cfc70466010bd34840b9e45d6f308de0635a567b786b88e39f3a36985732a30fbd046ba1d11fccec9e9b9827e402cea589a3e0f8da530040610571818bfc102a6580f771b0090fbe4c4b0bb845880bcc1c2fa7b030b9f259c8b1915caa08444e7724cea22c1f570186ea18796ea4132f7ee7c5af9fa14e1151382065dfc608aa5a992a4a635dfd4a8dbc1264308a96132febc79e37d8ffa48e1eb4ade51240e10d75196b44c44114bd0d33296d53313a49f71814a27ac6168f608c089c40f30fca14513dd48d06fb7923c4df2cc67dde270434a1aac4053dd49f634a112d375f90f1ddff3388f315ffeadb835e432a9e0768466557f51c3089acf04f4c9a79a5159aee368ac5a4e8be10a4c6e00db41b1189e151465bfe2c8ba84d1cda114fb92e441c203e1d0deffd4976cf19d1d552e2a66497214eaf385a3d1da3434b93f62a509d30dd27cdb3b98f3a2d1fc161c875ff393ed16c20b456ac4f2ab08f94ed7f56da0ad35ad81564c150ac80b390b3ca9a7f6a4714d44f145c432812a06a5109a2340887f0337976252e919f62387243e4f2997e61f64428e5b3e3c76a4e25960024d023091a1ef9a15772dc0443028c7c123ce2f611694d858e3028b718feb8831505ca129d8adb16107a2238f429b5177482d5685463145de02277058c38be48c6e7c64164434e41bd33dbb57e266e7c7c7795284f3f0b64fe516cfc7bae213a9ce5020c3fccd13a9bab1eb60f78c64aa05d91a051182fd629007f58cc4c8869260a10248da903839529f91831b88e0386e8485f467847f0801e50113b13302fde60a343294e785089cb2c09712e7ddb2c0566b913b57a045ba51c915d8d8fee259d1a3a16273c91f595a82a89748c9eacbf5acc6ea2678d50c2378bf079886623751713633d947c0ccfb0ec7916ad72f2db39224a7ddbd35ecc11018efab02125f8463991f9d277300368273a71af8ed7222282d1f184c6726f21d8a707cb9f60d7101dffdce06b86ca48b49f1c4cb5ebad12ed45d82cbc4c64d77f8997a8dd21dac83b53d4da0394e90f9489c2ced2e61604230e166b93ffdfad037c1375ea96fb463e04fed33b8adb3c3ae6b1e90089b4bc120be96da7213847a3fee7d82cb9c7b038318ec825a99eed6b3dd83910d279f0e2bfe90e826f6213467bcc434798033cc06a950c2c054608da93e21dde001d17e004a71ce00d0316301833c493bfb99275fb92420e3631d09e4b6836e1fe5e6c4faaedf1d09324996374ea5674ba13ae8f3909652a423719a604e735582a2ab4f7a0b87a27db57f23cb00e3bc9c4ae22ca582f7859a10a3199ff127adc3fdf9c0770c7954c77b0b07748ac45a8e79661e9e6c9328d65c8b5a0eaafc1f23145ffa58bef2f3b4fa5fd3dda2d4a583bd2646a753ad41975d6936eddbfac65f0487fb44add3e8f44784331bd19f5961a2811e6d3a95a419698af1e303fa84e21ac58e32609f24ba1098e1418b3ee754e1aa3e5312cfa094a5f029925f8aba9020dae4e468780096491297912012e92b27bc1b2c82ecbecf78132e8b1a59b7a46e5e2ec88048c7c211a927dfa0e67ad0aa7fe5995726822c0362c74b5218ca55e1c5690184f41b25329255cafca130976a4fe264e4576a4886fa05d26e8e3e9d3fce65e0d3b983051d95b6317ded0fafff634aeaddb450da3a283ef6fd7f528a77f7a28d87b38cc8a75abe98371023b9a95c956f7d2e0e67a7877d3ddf53b2bb7f1ff674ed395975f33c379e8045293b7b221d6e74d2ad19ae6a14e2480d3da4b41aae681df117e7d6c4584a9c4d8784fb9acabb164423589fcf9448ad11f8f9e76a8b6e52649b8c68abde8df1233738957f58bfe7eff5844633cae1560935f219d52ec42e33a143d9e9a96e21f81c75835610778886209cec910a7e4397480d62c0c7d7f5ce54b64e1e650c54ef6f8dcb2c992e458e2d2b7013d354794ba16e242d984dfa6d2eeae5d85a8f582ffe82a4dfbd25c7cb7f50326801f0daf71caeadc921e2208df4d56ac9995a9a035447f95df60b77ec00c2da4bb8a2958f00bed6c91d85415bf5c644c2d472c5b61fe60c583bb26e092919866348c7f75c6765b8d9c5616c74387387d1165bd4bcbed50882205b6f4ee9f1b1886be007e6816771bf7f61e97feda6d4937d13837cf10cba0a65208f07076e617756f972cd16b09b6a985939d885c8a7f5c9899242e88fb257581b1e5278ac1616c8159a23254e47d243ba81106bdfb75cc4903173a4702ce9b7df8c163722d470141c52782b95a838c3474175678214aa96d69e257663be913cd32e0543daf98918f643362734ee1bd86e0187cc5b2332c8f1e48abd4effa8985c50daa392995a8517afda90bb5759c401029cd76801cdbf0fa6e4c97975b9393584051339eba62fc08a500a7bd03909a7c1408ff1a65e95b47e965fd9c944f2fcde7193fc6250937e7429e9b91a4de4a579e184dbba2d41d0df4ea6c1d916eeea75701aa68ac1ed60665b2b74cba55566ca301fd23f95b544eec24314378c2f873335493b583373a0e5d47fdf032a3848b29a422e3d333f8a84fd42cc27e904f17ddfa9d9c711d3899385a9c629bed2051cdbce9f75f2cd4eab3e62dddd61e4e15c5dc56468de5162dc5884c6db7b2d1fb89a180eae4c191b517bb64facd35efa0f4788228f5a4869d5528e13e0d3eaa90d8262c4fc9839127747b358e9243bb4a35d59166732c72f5e41026a2179ff6ec89fbbadb72422e0e8d493e4a30d9071081521e0476029cee6a7935b0ca41bca98229e5a298f4a60c4c218a2508aae99c9b0966f8ad0876e6726f337d6a22228c8f05a2dcaa1f38a4c662a41d872470714a4410cac0e04f4c1da25cb23653070c45e96c75445210064ff82478ca9262124161366e82e99e4f5e55fd971dd2e3a05d1f5985afea964feef87138efe46224efa170bb30b185fe451f8174cce1a192d97133c586379ab3241c4daeaaf422efe07348eabaa5583ec50f13b541311e84b030423b6115ccffdd62fc1999817a94703d6375557d2739c08b3bdcf549dc7732cede11152031153885b8f593632f021df477506de71529d5c126e1815bb161bcd404b6543239dc60f07af19041e51e84c8fd5554ca5e4a9f4641a36574c1eb342081c1860ec125d836ef8408adcb8c005b1d3e9b2191a85057c6a32b82dfc73c65e1e9fb00cd8a4ccec6fcea6b2c2a028f9cd7d4ba9d375d1c5522cba17a93d9f64d54a61d196f89856278a32822205e16cd89e95d7e1e0204ef68cc177778667870577064bb36a96ce32e66039fc8b05e600827e50b40988e8e4017a84b095211778e5f7da341dc383ff3ccd904bfb6212d52c50b0c88cb33f977a910b948c4f986f027121fba9d9156d14b9961be517bf0aae01b736b7cc6793574aae170dd7810c1b76b9bd6a5e2eea553f161fd5da9750df79f395882e8d7f6a8ce6ab2944d7da810b2ac1403561bfe5f89e880081501395fc55c0123b0be32831521738d83dde2bc47dc50871bdeded09c6aca574cc5e9be54b1070673a7ef1154de77c6b15e8db37e9388fb2966c76ee9dff4261453ed4124eadcf7740dbf62f1dbc4ff3f2d9eac858720f1aa0b459a1022e79d209651c7ff28b7354a53efa4040d73ac06faa4c7dd3ef8eb054ca6ce13f547026b5265b65dc1e7c31d5ceb62baab05e3bca8a4b85279da4fd418909ea782fc3e9d4247926dbdc9c0d152944b55e1d7062084da7dd90a327d4c16290af818cbca1864903e4f24e29df7841f1a89cc79ca60d0c25d83e42ba981b94f6bbbdb3b0d09745faf4f55b25e47e58520f73c89422c27d962ebe42338208ac70945179667973ef0596136ed7e88a3336a08349cb8cd08583c3cd280448b1bf95b9afde7cb44f7da9130f77b931942cc8418b62967999ae3de6abe13ec0b17d91b89f3412447510351545947fc1f739d213a3f8cae0ee081355624747845611e37ddaa72cf5ee2db850fa307aeb453f3445650618e1e7abef7efac2d6584b6ccc03bb2f2875bb2e7241ca88f642fa7947448398eaa6b2b803d1bdb7decaa094efefca40294f6930416b39eb08e000b23c39cd4ea411b824e15680dc9ecacb35503c4d59c9fbc434f755149bb6c16d2dc537e02db0a988516e648523d0eabc229df1a0bbf4a809dbe49d3a4a62190877484611744ef94d103338cbf60a7008e877d00b82ac3d3cf12f6f452c560493c70e9cbcf081ea9c45d78b9ec062d781b9214b742fdae5b315c689f748701a48215a33af638ac8ab03a73b9b34ac04993741290f76723118c49804f1894ab34924091395b216089c2961334d39dc67464cc8fedd011b0d5615a33c7ba08db669fff0b0772cb4d8649412f3caee3a2d5495c89c94d535a1a0973a10214c5013255678537ac1d8661f2b4035686e953a925b089496ab45d98f19cc87c5a129d4cda39b0bb9e512e1b6704013e3cbf3d1d7e16d69a08bc078c75ed60db1264860e895a65a8f1515e00015f710ffa7f7a9462eb02d6fc933bae4a539c5e57520b8c88a8ce612e0ced910372501ca7c977f447b3ccd3cb88b49527cbe25ded7a77e672c88bb852d09f892ee0c9a0ff6534a4997055d491d43a720b6a258195a5648985ed1a033a91f8c0a2d92338d412d53ffe9b1b9881b4bcca6bbffd5dedb79a8a189a5ee94f470083bde14e9469305fec4aea788598b4d1a8371eb257cc7ef7b042c18fbd08ed2aaf2beee0f4a99aecbae973251bd49ece9483d0409488474bd4e2c65aa19fc8072290fef085328942db135ddb4136afce63b02ba293d703cfe938d8b0ff8d93cd3bff972f85be344cc782b32bcba334fab4f48ab8bd3054397e0b2a37234ae38ceb1d2db6366b16196858c6f9aa31e3dc6a0f0219eb9ea483e81ee316d85a38549c1be8c555f78ca0eef88c35ff597fa3224b86930dba66e26a0ea365047a638fde02fc3963632e5506e16c58207072dcfcdd124455ef75e8ce590ed66d1cae03afc12ff6cbd405b6b573367f0a02d78827c246037b2b0b9af58771de708b0f11957122399db19a3db555468a5f8ceb0467705bd7aed717d5fe5ea6666db3681ce0850d88ac0c2362983f03c19848b7149c148a10d4549eb08c2925a49e1de26ed6bf607400978f7f3c0fb0400364a432b063d6b08974a3e141d7945daa3c1d2282bafc2a2cd05c3c296d6cfa5f5df6c558b81bec248bd2f98bf8a5d10302de2fd8790f7400c2de0883a06c7080c40f8db7466b94e9464fb733e3882af69d56dd40ece59577fe4b2baa1683b45f78d5e01f6439b5b6f56873948add7e030ff905459d452eeaf7a84ca816bb0224c545f7e46ff2ec92a42d2e6a0b5d66ae366ec05c832c34acc5a7e3c2c63023ce8d5ae08bb3304b60dd83fc7877c5380042f04c4e5691f18e27a965a76312130666003f6e2c7469eb10149241eb9d6a3492ee90cd1555468d5585f4f622db00b488bfff77a895f8953b85230578ef2c1bf4d09948daefb11fd76dd40b09e92e90e1c21d677e916dc5c21e462d5f82e4744ec8aae17ee47471da55a838523b87dc941bf27bb74b8eeda833ea3bf8f19f46f9586b98a257536e7ffeb74f58fb5a4fa74ea57d26d5d2246fe85a4df8284933d92f65b63bbfa0ae089a4ed582ed6283210950b385ce2ddc21f9a798f810aaaa3d61dc9891f20c3a318528563ad4266525498163502c5869eae6a1d153619b58250066d392a344c59026dee0b716a01445251510bacd13cc61646dff454a1d6552d04f6456b3d2a9fb51e1648558cf577ad30ad8b56f62e8db2b4ac8ff34eb3a08dcb4ee4e0f8a59e9b7a1c7936a5e5647fcabcc85e4f1c22a7d72563b26b71d4706ab4fe8a3141fd6f1a5835bce243770dda93a60f98b6277b77cf2cdff41c2d73478f52b0677a3f419009ae687cbd5b5a487023a346d7445e0233f9e982643bcfb679ae9306d7f372cf1df0de5d6d7509c5278319e2b313274a62cebaa11982f0f8f78c37e1317e63bbb2b0fbe5d42a094ffcf5d0ba7fd7845f0d8a85eb76eaa99fd875ad092ef0cc2061c6a249f637b31f638050f27e86b76b961f6c66119f4af5ea5c3c4766991024baf458f4ab632833d8f504441b05c1a962ae72aa5ce1a4402f62f22daf280c8e97cf64318703c46341975ea1680780b2a049d8cf153674fd0f6f436315e4f388a5e6e4e3e92102763964122cb997a3a4982ef286e72b0af264a0cfb6a6dd86b4a6fda4da48b8f14a92bdeedcf80563286c9e366888a405e6ef18f4be5af17b3cf32d3636b5e9534d15604688172b15fb84edf5c6498b4e5e4351ce11ab47e4bf71f54d8714f9f386d6d07325846b2f5fb42cd61080f0d452a7fbf9ac33a3684063f9e61c4265421ec15dc2a525fd139bc47e3a3b4eff799791bc26bda7f00c1b458bc7ce0bbc583bee5276c93e3bcb9c5507ea8ee68663b62e6971d6863d9b64904ba9c9eea18b5ee12d69ca5c9b4b2911b2da489e1a865606eb46e59b7c6346c8613c49e330128e0f8bd112abff3fb213e4bce6a3d33f020da67a438365be27135b12963f121267fbf5f9b6108a879bffe76da37fc8be5fa6a8d037cd128f8defb152ecf540d96121e266240d8191e24102df120631d632ee8921558f5b78d701e0295284ad816739b7180ee8f1c7db85c154a6f03644523701014913a102cb687a6d4e3efd5ea182fc0186396707601c6462e6c9a43b4c9c4fe61f262482e20dd837c4a59140f05364134bad477d65240a97115127203f85e63bcb1f6f4e1fa030fe2059fb0f44fa8c3bfc472fe0867c8e5f649d5bac060b40f3af20c4b68aca5ba2015beade75dd21338216f7280a14911d570bb20cb593d788ec1e5b60a556e9a14a6ccf73d5aa7df1793979d6660fbd2a887b58b905fce243d6b6a42d3cffb980806d6eb75132d0be9655b708740ebf7a8468700340453bfa88a3456a41faa3fa55c36ad41b219aafd0f4e5716c85c9d4d642c6cf1c66b0a99cd8800664205c166ac71772941c0b28e250711e539dff2e2c53a5a0c8ed31a293f710cc460c8d4957e8bbd79120c88e0bb4affc69203be3e3ec6080684d0c2fd4dbac22ce7c669ff42056e50160442c669777ec3886078226c11b51ace574ecff544641d77e73ac1203d338dbe94286d5e102a8be8e3a712fac54255d5a5d1fb4c689c66ab63d8e882a66984cdf7927096d55324671a8f1ef4ac71c8508597666ef4b20037e45f01b4a49ddceb03c43674c98ed5309510c21c663df3ae68e71a313ef80042697c33705dde84a6c556608c6c0841f0a7d2a5c2f9ce7f7bfac9c3cc2390d120573c69b893b84076f1fd210f627ddc649df5bbe5541440c59349a370c9851b0925d3d711b27a8322e2e590cc5d02760dfb01b1e37bcc0c52ab23a8707e3f7ab227e0f6e90e233f50e52d0af534d49fdb50234fbeed1b0b14292d6621665b036fd2613d553a2a6b066105a88959b29334a2868582b684daafed93c09610f214f40721c839508ea1fbba1fcc44a529a6b95717178d56ba78491453316673bd465decd526eeb4c74236b3a98ebc4ba58c4b2a2b4e15309deb70d1651ccb64cceda3854085e5250d235b740c1bc79251a93e697f8c3ad689ededbc580bcef80e7289857ea5175f95475f75875b1096fa9060a7720304a58373234046a05163c44d59406f595f5c737afb64855728d7c99a9a11c1ca9568984d437319d539075e6851728eb7fdcfc026ae92c4318fe7857bd317ca19a08a666e5cfb0d511e7e47937c0cf0bf418583fb65b13f7051ed4f15215509875e97d130f9ed08dae73fbe303fa7bf2ca824caabe4d2653d2f76eb3a70307ddab260083b06196f7c4c9b48b66d47c7e22fe879ba0e15e7d767940ec9ef172bd248a3f75501acee85cd5fd1a82e5898d6c3e12a7752bd1177edbf57503e413cd7d7ee226b1aca5e49a4a05c3b4e2ffa400c76a39a19b9b2a34e61a847bdede1c3c96713d46cfb6e0a2ca4370dc593299d0465cdd2f3803f87aca10ac4e2be4a705c9a20432e492c9c5220241272b8f95e1bc79a9df7b5adceb24799273dd2009e532dd24ad96400fd0bce65ac17383e221a890cc22323a4458d179b9885900c6ba4159f344c275ece8bde12ff9f1ab67ead7594617bb4bd4358660ab33f9bc6b518f7dbb22a7908daa5fc2d3b9419ca7b9c86c587205f856d049102a4bf2f86dad032478222b37a799e90faa526d2b995cec1185842ff101e6652964158a999facd5db35a203c3bb836ccca16f5e05fbe0b8ae2a2e5c33d150ccfb530ab10863c62146c3f0a3a18ce9d4f5de1bb2c8ccd26cc31bd689ae9d0edbd93a21f17397aa443203edbbf4948fb1fa2d5c4fe6c06e7958d2da9596a49d8fc10340f69973bb89485496c943a7d91a8f53784967b1334b0f449e3f7a00e1d793eb5774c0ab4724bae01a502b0e34f2fec1a55a4a6f061269c6742fa3be993f18ef9edda7b82dfbe18cea04b709829ac9ad0f40accf7deb1c804a44ac2d92cbb1281f79b16627dd5331250e64a28682ee93d11d3a132ec69a2ed454c7ba4190747725d05a447c8e408b9d3df85a53433ac00fa84aafb98d980a44012edddfaacd6d3d739aa8cabfb9e744f524091e91cf7768679667650b0ffa044b62945976eceb55f44202601a13da6818fdb254a4ac316430d0e1555c6ef3f913a56b604e7be98cce8e3a5296e3f7d3df9749e57ca0ba33a963579a633cfb9c583f7b13c645a824930098371d80c4fd990b03923755e9257f2ef26266bdfa6e6b9547e3f4c17d206d1d25a9b43118dd947efddca402e2352c7b2b8ae42502cb7c914d942fe2267aa55149aa7b6d948308b0b9cfa96aef864b19b86e9cdaa108fc5dd803a0a6ff76c7e8126441b6c86cbe35d143fa07ccc7e6838e7c002807fc171f12e30c3b024240e58d82b132db072166ac11e786b2ac410334452e2e9be28c6018d448646c338319fdcbad9c3da6931e35bfe9dd2b3f7b36918c5d40bc50583522d2fec97130535e8a130fb36d3edadb4c13e781a3786f8ff398d2096e3a398e1dcc1989c030aae411897c67566e80e73e22ab80d190421117902ed221cc3df16064212954a90f1a6776b4f03f458837464ba319649969e6bb5fa8a60a724081dc1e1a05739daaae55551605fd7666e72dd2b674284a056253c80ef23f6836b07a50885c9df883c45035249da670009e657571b05adc6501805936a4d405e2f47e211a5fd25406904b7cd7c16b931ef1d686c12673005a10c3e118e164f4288529a41bc8df4b9911fec14650723bc685de841631f2d84e2f31a803657c5c68314cf2ff84f1fcafd37ce4156b03ef27dee845180342481f672e4bd4caee18770d2eefba5e4bc650224402e1ff98167abac3f0f25802c4dc30a020884ba15c777fd3ad2d6e45e2867c3b256380900ace462dd63413e898fea4f43cd0001809c7a91633aec07b7a91c9ee5b3aa8a37a0e9ccf2f9657e2dda4c3c823e5f35d8db91fda75bb63f6d67eb5c2928dae388577a346b41e7b75b10a0b002ea3c96a2a5f002cc04206d41cfe13a4760cdc11e0353b6b9d61d28e8ae8e0dde0dcf6b36bfb745b005993915422616f96254c00f314d08e48a2b5e8cb6f18dcbdae3b938874be7487007037a1db4025899468e30f41f49aa3170893c778ed4d96da46f1e4ad641a697c9586ca28438029dbd3f8a8d7d31ac3e41aeaa2e58778d14f79072a4272064e7222317912e9ea1c607e1f9920152362b720f699538386dbdfafe5e39a7dd39e4a9e9d49d187bcf0e71ecfa2db9983615062d57ec4438a2ede3736e4bd1ed2391e92f31c9a1bcc5de7a4f0f6a8116fb33a842c863bce20d6f7e3eb59a4c8ca3eb8f0a86071a10cddcfe44cb3ec7326b589820f942d001bec5afd549e1c02af688734a141b75a3f81eef5b60bd07b728c9e6d4e2ce6998e77347e692b1f588de4fd525b9380da90fd60ebd6077ba12f7de2a37cd84e6ce3ce43cde23d9d48a9f11228239fa5c6ebf3c91f4d287260a86d48bdc06eb54567ad9b308e4bda39f3905bc788d5424af871e2aceb5b12357438b06ef512ad9c5e8475847192bb18f067a6689732e452322a59ed585b733b11d89f49e11c3b6f0be7d878fef9107acace6b8a205b6d7db7c5e5150e3ef1e92c44a3c36c6ec4798873630f175267d521b2074f5d3ec3a5a6f1266b2b5caf7a8d8c4aa5f799c9f7adf27147d20ecdd2a324d41c50971efd8acb741aa9769944ea31e5f62632bc887b3329e6ed828c7fae7bb34edb5b28272759ab52e030250aa5ad8e6183093c7d71f843afb10d9c8ae5d6389a225365bdf8885c4e7f95e96f90c2b32918d0e7dfe56b14888708e6353ec8bd23fa4557b0f45941e8d1829a7bd11c433314ed1cbf7c6e1e7ea7196db21a51a40e96954daa3d4fa73a2cd3a4752ffbbdf1654935e7b0276b217640f8f601ec7045381aa1203a4ee6be9cf8277aac329d882fd3f7d0edc4d610cbcf90a10ac67fb77934c5ff420926818e8ee262abd239a3030a1349fda1bddaf32b27344e6a31ba1bab209ac4e012df45b7198a88755a7e4047017cad0a2138c68322f91f169df774ebc35e0b7d1b321d7d7973c6fc72fc1810236dace3d1b3da7d5793f8d087b4f0abb2f96d486e6ffb653fafb3d7ab8442405f472ffae980125b7781f9a02622e5a25da6df656f8ce23a3c3976a9f7146346d4b55ed8e38be0c8c3e10e445793784abe5219de8beb89a63215b81ae8437cd4f0222179c4050693fb06ea36d9d85107207dfff76b465e65a8975938cff34a67d359daa2b077df0b49738aaee567e9cccfc0c5ad440a01bfcf8a1e72ae11aed9c922ec9c2196237347360b6d50b5fe40e2990e2f7f109074915097162a0b32be32eaf70d2c28bbb68b5ae2bbd01aead5c92ccb4b43ed05390ba61b024ac3eec429d65a4ec2a8236512747e679b6e3cf532c81ee1dba17dc7ad852444b430e364ab321027bdd45d5d6dc76b92aec9c657d5eedea8c89910b9d02d9459049f1db572710679c6cc2c36100c7aeb0407e53bee2e6bf80cc8e141fda7c32127df7a2ef53aa97c0973a2a40edc4b5833f4725addbb0f6e7df9bb2e620f4509935d2c0aea9233422f4c84f85d1346bfbd33312ecd9b11517a58497648728a0e3cf299f7197b21c525f4bd7b0ef23cb811f7b1395211dea65ae3f143a71754e7b53c14e3b8b3c87f98898cc1b1c6758c0ecfc16f308b659c2ac28205efd6887da4f8c54e9a07a649d7dc5a505fd5c1fb6bbc6ede95519f5d2f6d32c6796a3de6e9ca9425398ab2f4d02c0b481502e11b15393cfbe74d5aa8696b9eb331fb8890b4510a7b1386e81b42e9dca1e05148911daf3688d03d40292a942ab3d1ff472f5a1909012e5c3ebbdcdb5a959a6898f1d80bd66dd73650bde35db5864174d4de940fd14bb73a62007371dd9a954eca58f1fb8bfba5888d6613fec83a2cf4d7d665eab493e89dc408ea6f8989817821eef368dce11fbd931ad834f15ea41b9a5e9ab5eaeaddbd747145df800f5f20863b3f5928b3a50e9ee70b4dfe48de0f1a42a00b81fa34bfd6ffe44603ad856cd098a03288f23dbcefb15653aecd870b9f09fe134d7786afc2cffce6ba5d40d6c5d0d74953481773f239bda8ec57a39027f1929ba51bbc70995650e03d1393094a8b0019bcd6d1227662f33ffa8433f63a2e86a793bf4a1fdb7a8e793dca4f30b41e81b576caf6021979f3c804a2d225002aa4da3659ee72c74af2d226582547aa8eb36cda187ac333247becedc231433eaed884954e9f2a53194468e95d6add33a12660999ac78c4bc0191af3dfd96a65ef5fd9f0677e0a17d8c860029cbc6b97d49eb3208ce1530610904948e115ae9e5dd180769e0d3075892076806698595185cd13f2c62ed62dbe49240bab4010a7efbdc2b2958555c482ba8ec415039176803a1958ad2839e5d1c412838488f10956902b0ecefbb9b3ebfb78691db4f614d6e954b1f7581351247d614a0631afcbea48fcfb31e8083626e2c9fcb41a8bdd4f5087419b1b70ef217456ffa1653f8e49c1ae72ece07f6859905ca2fc8e063af3c74e3b51918730a6dd739f902ce85c780981155c0e541682280051d0a5c37a329fed3c66078196623c934473ce818dbf40f45ac64fe474f6ab99f3932ac416e0f608ba7a348c013db9ad01c3c13bd28b9071bea315ee62b797d63212058030f70bc597fc673e9098c033dad74f84c558c3c1a20f7e03b5c6e362f1f82661b7d02e2350079e7135a468f82438d84f4ed2beb4467aaf7511b1f27b22078d31698136498a0d7ade69ad99f8ea7f59dcc70145691ebefca0d0fa6a87738bbeb5422de4dd275ec1caaaf801f8d820c55df0620712e50c33f8281ae7608ade778f1180c1a2fc7a28234f943f33708a24adeb91291160099d6f59127dc05b7aff90ff2c05acd44e56c4bafd1fdb1a1a9085506ca34b6db5309ef5e8e7394bf71975b7d3d856cc6a5172749c45007987cc45e07ec05783d2a65c172f3b1b2d4eb0cefb6aecb3ae81d3dcb8ea87e220e88bb699b8bb48ed6929e810e93e112f1870438e8ad7513a24cf5c420598c17702cda8bce36eb7e7207f6e5762af73ae185444a5179e7021fa8f19997422ea6e072dc3e0c742b45096178c0e4d5991ab571af37230b450465c1a25157f56f2bae2173bb3b28736d9d2277f9ae9c05ea995e2cf3a6ecee65c891d3d4eaf95be208616c5aa534910938c1e7e3308c70ed5affcf6e07c4910892b53cd8f9d04fadbd21c6ffa2e53d82145a8eccd0a416322858ce9cddf098794d89d2bd63036e74513a0c3c3b17b416f0cdb53a12dd9e1b1b748683dac609776a1abb9782c0d145a12708eaf33f61b764391ae263b0e8b48d8a040064b2e4d446066260589115f304b7643f6c6cdca04713fa2e35479c5a4462b6610b35c74d04f3b3603e400cf4f4c9c343cb748c00fe64d1cb0a3402935c1bfe87e2a37e75f2c4c4a33067f5e9f587e3fb5a8215a33551b92659f1e1364869bd221452110e4f32eedebabe182f1b2a6ee658510f31a86d09b5b87ac4979c74cf23d33e99ed1e76d8aa0223b01daf73688bb44b883924c81b9e8fd03dbf6207f35e35d4ac42833423ee2a94a1d3df71b8c8f1ddf549673d98231fe9d7e5045975c0923da374ef874190c6bc615add7c6f9f3badf9f15ae13443d7ae163c77e68e837c5e634f545d78019c1121bb3b5c26a97b64aa80ad45f225f16570deff831451321dfd31d5d13e5e73211958f9eb553ef4b6071c6e4d414163f11dd3f06345ca4f2ae3368e97c47dac52d31c58b8e9b7a504d6e7b8468ed85e967b382d041d04f4c830e3084153004eccb0b947b1b3f20b3c84f129fe35f5119f76a2dda6d2d9a3d6841c56e4e086e02396ebf037323f2a7d67d87a008318d6a63eb68e62d7a7d27000606aca657ae25036b185bcb17dc4eceee2346ffd33e773ee7d943409e14c50f56aa6d7eb1a8bb1fc08a4721c8a5a0fdb4d4236f590cfcbdab000113b41b8456997a78d317c5acb3cce26edb1b0cc01bd92ae5df94a0a82048c6b23c3ce142c09ad8d94adcf2549c25d59e63595f273b57a6c80809f4108981f2986ce8bd5fcb64a59c8d4d45f6f5585a7649e92002ce9c8fc9082dda948e7b00d70e91e40d2e753d1effec01c27580099177bacd92eaeac057048b771b6a61ee14d0a9faba1da11f07ca83c36ed46cf471c4bf2b248bcce2f3a6caaebc4e0caea8d8bec93c0bbf61497fa60140efacc1a9fd696bfb99bc1f9c84f89fe82765a328c3e898cb36ef322374d4af66d73ec1c2f1aa25e4ce7587f2ed05459d6c20e4918024880068c7e6aefb3ce9a220a6e35a9ae7d61931812472c96c992d62ccc70e3a4d540b772eeb2568cd7e3203ee07a61e1c56c0509120cdbc126ba7651037877a51442e9d3b33d417145852286a9d6aa076dac1221ea4a0a8b98cea3368365b8a8e2b43594dad370b48d6000754007158ab26abd1a4e94581655550c722613640965b91658237229f38ac6ff998134793fa3c99381bf100e9425564b4dc12c7928a66917698f94a679be9b9939d2d98e9046683e41188563ea5fcb7b3cc06cb462a00e0c1e33e21ae3be842b8645e84ae2aa2b57c03a9067763254f05f6320f4da7f054a6dcc188ca761b32b4a9e56c1b119f846819a16ad630bcf2ff9dbb1ab51b86517592114f003be29aabaa1f3d8bedfba9e29c46aacde6dbd8c2a400db016525e65eaff8ec3e93fc89f8952e334be13c64fff7deb33296b6f6889a9a4cb02cffcd0f0421121b1303b898463842aca105754507bf5a4c235b1abb1d991b41585710ad7c406eba65185ce9c8fcf79f419d71582b9b891815009744ca6180641d22fa6104e596ea16495c5265465a91af9250a777c9ce108e176474c66cf82346eb77f198c3b8bba986ca98bb83772f4aef2a81f16e9a33cdc0004c28331684413e569e1d9d7e1bc944198535455beca2834faea444ae2a2ecf088f74dde9bc31702151aa7f81def905dc3a218db3f6324e2a639a002f4538fc81597a6b543929e8c749efcdc20515b42110458e4076f7b9eca44952e41ab9c011e21712a06004d1138a9620b95d77d4a4b333d63ba791441a8a412b4ce3c87233dd9576300a16cb841e3e559ef47cbeb1009ab1a15b68c496881cef065a8488135ec16473e3ac060c9b249b960149f8c1561de020ba5b594a8ff0f2655e57f573ded98c943cb1c94e00886b9b0cfab744743e265154415bb693ed40fae6b8c2c2d3ef94659af292854746b4fd2a11616e51745ad46217c43d5136bcf83ca84651cf90b0876ca0bc17ccbb8e10e3cd82a7250cbadc4dac7dfc46328909adf041933d1b34ee72f8a3f147d12032e2eedbaaf9f6ca99f24ef4695d534927340d3a0a6d8d5685a76ae731b753ad4ee075df1627e654f0ccd1512c3c510a207495e7f572484dc6ece5d28b6dab4a0849607b35451d5481ac2efbc1970a988b8a023634b4ccba11d2228e112a827ef2b9b4257337ba4a54b44a1317535f7d8ddf20a7c65db6606b19a18eb6827884a5a9ce233e0ada9b2aa64934d65dfe59f6e23c4490ec9d255c3e5cb3f2915a8c5f114950b3e7edd01c60ba4d578c8d7660ec457422075a909037f339592dbb9579efe137bd265ab4de8ef51d55c593be15a31a45d10174ded10d3a98ce48f6702baa7a7e29c5902ec20d7d9ef9bf9e079f7f5dd70c3838c4fe1005382a805536e12f2bdda2e3aa0066ff5e6da1f52f5405a54c98da709aa3c12c4a2f6cb4cc7c25b02574b065a000629f3dc9d23a1ef32658f29dbd8886e6b693adac3c68031b4a8901c79ec651b57a11fcd981a7b9edbd6ca80eaf1bad50533d6cb188d3b926abead297aec34ff15fdb24e0e35ad2f9345e31d2e7a4f2b3a49becd27dd49b9f64ccf71b34ca1f7271e49831a78c19d67759a0a0fa7730896a02184e60611f50487908a7e187567dc2f4110cc599f61dc3ec0769fb6ca774a350bd05688ca8efd7cb588a6e4d122d389344873e72517cfda89e843263dc2c20f435254e0c09c7f1be879daf29f289bd87499eb5a0cda78df619763109a174d63bae7de3da27c65881e171e4cadd072f7a18d0aedcb09021170ebd05e1b31403666b090a1041d6377b1189be20ff8439944036490080fd4159828afde971f0766cbe09e613502d4e558bf113a961970107cc056f5b7225e079ba4020d90f3900b6c5dfaed3315b30216cc972ddf4a5b2ba9b7065f356a00689641ef752261dfc2331335b61e885d79c462650f5ab8b30f62e4a692316e34f580c7f0ae288e525a207e19c3c267326e7db6c0a37a8314d8989a7410c707fb9abd99993383e82f5eac4c47fee581506054ad9749c1ac3f1690b44628946a3a1c554b46680e7fb5328e551750aeb585beab404c316d5b2e1684e2fe05c5afa3b5fd24d34a902f0ddec5ee155e4c659b11b226c83068c8ee44612c8e4f5d23b696df8ea017e0839fb821298799a9d8342c9ec21a5c567b96d4260c416c61c0d3c22a272825e3d3aae152e3f92641ce7a8ef272e26776823584392fde0997c340f6c66c0ea96990d3d7303871a12357073e696406ecdb9fa62a1114167e387028b73c7a8c00e0497e5110a434718cdafb49b63d0eb61278f3ebbeb34680c501b432d557e8cec3cfd059daa6e1a155532f85400ce199adee4a75c545c0d827cd677f4b96f51f3f99eb3ecbae669a7fe38b41834e53c7ce3f64256665e30a44826fcba66fde327fd4568bb3b5abb3ca5c7c5aa41b1f6985bdb0b7c630459b3c82490b132d4ca695c11714efd067bc67799fd934d929a4ecca1d085da41cee6ca40303763ae604b6f164a044fb8ff85564d7db4546b07213b555e5422209c05feff115e7d8e43c487800034dec77ca3425ec11a38497b6bd53f5ab3164e23dec259be7e17a13db849b3da608ee774e4c4723aac015019f460e4390a681a15214893615b50d9902df815ff51762674a2a04989a2036abbbd2628b4f04d888defecbca3558a25b2efaed30ea7d316adcdacb82caa925b508a881b4b475777429c02d5a947f769534c512e4f0f3c848cda96a656641b94c4ecea88ca62bb77a15b8e0174d909ddb7cf3d9632b1584582e86e00d647bd20358beac07f6d866fcd50d87235064c9f2d8a3685c24b7b01d3fbe03f83450e876fbbd5aec03cb8b2a94aefa92933819f70905ee5be14c1fb270febf28e3d440e0309c34e20cb4d6f5664c696ca27d794314703f38187acd039ffaec7468bcb60d103a2a8d5ec733ee5dca6b78077345635ec0113b7da6f4a0e57d57260468b6e158defd265ffb21d5f466cb3603bf8aec644518ce5455a9fcd288154ee614cb1d53f457117b76991cd5157f2a4c6ba08e002445967c20c021a560bef436f0df68fa19577f4be7d0c3096387abd53f42120694d10d8f2041f005e03223baef00cc96657e4d6d797f0791859b76a7cddb3e6683c5c5576eda9f2ee06ab596e37323701c764a426beebc050147ba57b0d45693a1923ae05da1893e52b27b76b1a4cd999a7d53d44548238403570b53ec7489495d562dce6ae7186efcf624409f3907f1db3e647802999ef49583dadbb05c89d1b5d5d914cb8c5ab6c0c83d882603346687fbd704cbf0197de60720bcfa0e0a54ea60af1de7bbbd177e57ab55292b3bd0d181e683acaaba986dafaf7f80a29814f359dddfff74e333f69932c12cdfc8335d9bf75c0226dbafb9d64983905c5e0c3c7f4d4084ac28e3c319636d7057a74b015da3dca85a12ab34db768145daa8c3ed78e53e744b52e6912475f4a389ad6ad9ea1e3fb869272e407ef332781129f1d57656fcbca8457b0d7b101cbb4c8d417160d9b1a95aad3c18ebf33c69480b78d1a5c9b2b7d3ea96d19e732f22ab8e5ef6680e42e531fe877a08a72a494ecbc35ef0ebfd51df46e9a08747475a82b4c1590148fb9a1280e868e33236d8b9b6d554474876b3ab2b097eaf9d2e683811d5a0a81ddbe932eb7954100218f605c92e4e29514cb1cd720984834d9433e0c082ddc188b1637ae417d12978c84fbfef46b4c271afb378aa2cc135b888d786e218eaf6db4809e5c92a7c8403a20154d2bc5cf4e8e7dce57d0afd25fdd23a334c6b9cb5e42b49baa69d80a7726910025d3e6a910cb09a6742bf5eabc7ddc239ec708a7d479d1270c7b02993a847b0838422535a66185f126bc67aaf92c62f67dc1658688af6aa2d6db5d6c5007e5afef2b942c57f7ba2e9e98573a67f777c42976f699894cf3774f922d692c6bf609b347d4f1e0dadc8779ed2933384422520993e36ab0e703673a705b888b4e4a48ebdf19361c644ef533875a645a4f79f2ffd8c66b0803c172018a5c7c0bbbcbe119c202ee4c411d898b5b79a8a1f88b5169818e11540132471370f3147ab90ca55026cccdc155f18739b556509648e98355ce4c6c84f3995bc01905a90f031b1b704728cd1516c0d6525632684c70e9cfdbcb7c29be3adab61d58c7b2022a1fa0827c210c273b4a0602c4b0a46cf54765115646f05e9cb37393d96390bfbb1edce2a19f62f0919e9c0915b54629cda95edc639743d15d97b02398ae73fb22e0c8a0d7cc90bb56aa6a48d5940c81e32d94aa5da67558d533b7fa966efc3c31f140bf4abff9f890f0e1239b9e46a012c667573f41ca84efc0c3b3f8a880b65244437f387b02d9187119450cb2b6fb4f8f9cae967b2175a15d628768eab9cf8e401a7c0d68bcc889ddf74c5ad88f384695853b9b9127ee3e47309e0d56bb6d8e4f05c274de8a641e4d0362db53cf343d7fc6df58b3221ea0e3057d08d07e836e6eb45c9eb9170ee02cf54f40642f9ae70f5b04fcd484137259f87d751538ffa2850cfc07072fa97297124443c0c34e25e3f90534b03e83eb14e2f3d6427b4a320a60f4f860a8571c17ac530325bf26d2598a5d60febc83917f695801abc1addf87de4de0d28c43b10e89c2c53edb5baaa8d67c4ecba7ad291432f55aa180eb32629d45aa76779a45a34297c7cea6386e4792699187dfe58cc02c1204ae9d07c67ba6f440e5bb5619bf251fabc2afa95f85b1fec4014f507af26c9fb13355bafb044552a7d29f0610294408c3101528825ff48490475498c0a8fc80c2b814f6140876b2908008bfdffef5a30d51b34eb1a6fd6842971cd80429271dc5d27e1fb37084b048ff81f6fa5572eac2a5ae655f415f21803e5f02fa9f9d4620dd985bb5e77146eb27a59c426939bd4a015aac4194ae73bbe5cb3e96cae9cfcdaf77322fe82ee0232078ce6840810fa55dc6da8b0f8bb71f4f29f126c20729baec7874d74b766fe30a95176a7a4e08bba7b6efe681d70035ebec5273c2e14815b68d13d15cae342b57d5db17234b40836b5764a7d37a87005fa1220898484016f6c3459c03b25c1455954813f24da84c2f4d09e52091117a9d5195284b2f469aca4f6cee1720535dc132ca2df95425f4cbc5b65e0d006bbd0da22383383cf7b2fd1a938833015449ac5d2c58aeb4abe32225a59733eaaa9fb444401559e78173a500fb1397130c3b9dafac8d6133c9fe986f45d2f56b656249570156b91c09239844d8d2866fc99974b765c62798c8e4cce4ace449697b0663d571530cd5a5e53d565b4b6ddd80c4986939764f1af404afa1acf134f917c31b1edf90fadfbf74ee2780490b6ef24c4803e7e37822619e50b2c134f0aace109b228b1e4096c16eafab3a5c79af4095c81362c278b0aa30abce110cd564d893dcd890867c49bc7f36864405d3fca361a22d3330f51ed5a45eba0203072c0b9167289eb494928b31ba10e51d84d527baa5a7c8d8290f27488d3ebe71168f8683b6b7a1c0a226109bcf1cc8e26726c8721425916dd1f0206b8edb573cc3a7df6ef1f0a053ba3d9465cbcf2181bc2ceebae692aafd03fb6751ec0b259dbcf59eb999d3c1d81a5e344bcf7d6ed102138e9592ab584e63ae5aaf5e8085af54e10d839beee707f9e35458872048da1d2eba4ac7e1776ec3bdb4c3f064919cd0d0956c7d841d93ad15f7033f2108c761d1b7cf515fb2e2ac01eab0dc8b73bc74c2f0b1a7f0b30a54569ace9d2795049f51ee04b613b89b544cc2f9af95f68711c6735692a82802dd9a800de6fd7e0f016474598b8f1bedc5d4c21e8e2314a2f4019a321eb1cb49f527ea70de23808263783ddd2f44781ea161323a639845725e18499fe69b3ee13e59e0c57e4208b5d2576ee046905b089dfbe702ea914113d71e5e745a392e85869fd7d732504d2fe70e99c17a5e0d9a1eeabc8447c760c82690cc7f3c134892f2e87cdf3f12df54016432915fb074be68a5f23763cf3bfcf4e30d963a29992b6cfb48d61c5b3a9af353e80831c457003b4cb1b6f919dc6264e6c7bac100555ce0c66df73a97c4947e9385df8f66471e1c3c11111b21c06474e0f5bdc853c8f484245f745f8ce91b7f62549414d642619f5cb2c9a83b99e6c7385462a8b86c3f82718d4dfd9b503132643953c5528d03cb2490df456be013b459e3e6f1b6f60d3595b89da9fb45946aa2d89b5282dc6933dbe01da88d2f28ace719d1e94c56ad33adc66cb91f34cce61294fed4a4e0c20293b6cc0d57d16cb9d20da59de61ccf39f47bd4d8753773d7b06b8b3e8e6c45b14d815b0649a4954646969ed5294f645310a675ca1b642e882cb04ebe44853907c514d580b2cb6eb65db2b5e7696e696e6acbb7fd06eaae9ad9feb0b04a9e6be3bffa9c1dc85f4a79a1f89cd915a7583cbd15b90c741c67979c1ff2d910d56be4494d8ba8394811d471bda4332276e60ad1116aec7888f63f5d9d74a32a6bdadfc2ad812fb0d66222e5f2be484e9c2bcdc4392f6f5486e773e169db087a13b0676004489a4c5a69ae68d4fccd5cf51e0a57b8185ebdbe6cb10719c5de8c289708b882731b08f812589380a1334b87e59a9188afc6b7cd213b001db2ef084bfcb6df759cee38159a208e6ff914093abf5e04c75b575c7c81a63608c09823aefcc08425fb6777c0f20fc51f2d85ae78bba4ef94a0be48924522b4e0ffbc00033834bb263b1888d8cad9ff1bceb543b9165fee468b7128224a2e2d5048a005e5d5bfabbd609871a14c7d8e69274240e830d7a52aec38da4a20404c9948f34a3cbf075b015b03b3df12941bcad7b2204eb5e28a503385d09cfaeb596f5c14bc9a7b1f238e7129fb387bf2c07910e111ff6407af3594b366ce4dc26fff632a42f70e2c75539da82c5f50a91854e1883665b015dd25391dd7b8015000e2e3d3bd10bd08a20cad809860865725cf4f2217ff1a96f0238fa7dc8f079ee281d70fa36a1bc3f1ec9b9869719ac173efc04146b44edb4093ade580ff7665173020e494dcfc323e328a095790f13c82c93597b65f0cf53be9a28b1ad7b72d680d82f35239582679a541b24e774c205598fbda0750bc1223d4673cf07b3c9160da455147a843254985462cc5ad3b91d058c202e81acd29638b56bd0a5f0ccfde20b06b53a9757e403509ee0ffd79f001e50e8b9b7fa57031184f34a5a002cd84067017ca2fb570658097d09f6741c52aca0afbedcb3a701be91fc09abf68c2d21c0853eca220461cf912e5b95850147f8a2c03e1227630abb716a7f85e32c2ac03f42ab757ac6b89dad9b97c2b5f2f26882f30111b6dd8304562a32caba36678a2e37930369dbcbb9a92a5af0ba619a328c8216621fc7ad00efdc25e8ad6174d9030cd5a92c96d956313abc6dda37633fba8773282727e8d0171bd3d97aade62a66eed14de9b81fdfabe6704ee9c752224300f94cde92421d3281fea66056e2110f35005183bb43911986703f124036b832220926c40a889cdc31ad99b0a71e0c2efc906227380082cd99cb2d967c58b4dfbb35b8686a7a28644fa33f28142f267ab74f561fdfc349de85ba4f2c7848a0370501cd839709985b2867007d0abd8661f07404c95ff7c8d7cdc731f0cf91ecdf8da278af7dd5d684e81e16f8df700158930df03aaa228c0033705660543f19787643adff10d9c0586069db7c77a2e4af549e5b13520bebf88fb4552108a632035d668d5a0fadfb91397db73de5ab068a2df7c0aa597a3651835a7bf4b7a26ac863d1199f2160e9eb9e46d74c43b65d9d53eb46f727a68defadfd0213b1059482a7ea47baa573b95b18d9a506c349477aa68426825adaddc696877e1a96436cd40d76e097c45102bffd9dab2df6308c1d1f71b4749288cdbcd60513d431b250b66cfc8efc2701162594bfc53d06513995154273a99c3b4cc24aaecb9709d8a6808abfceb5cc6d7fb3824029475b01e46d264dda1fa0782b2cd0da890cd89caee02d88690746bc3b252a2d6354eb55b0f8808ec5690833427788407a89af13e20520d73b823fd00d17630009a3cfe1c2dbe514129f9db03014aa41ff07a90a1091a6a4d2d86a53aa85de27d10687face8aeeb2cca2d87cb433d353f84a9db0b2f322ebdf4e69bcd9d5de327f49e795b8ee2003ab30bb043c634ca22279c61f8bfd27aa9dd70b5b55a80be05bd481e64a9ba32bc5660b241f33d1546e681b5b057bc7f556ef4a3882c723d1e22d9ce93969d04898b65697a3e5a4e3353ca464bf038a848e25a6bd0486d5509a427d731dc1596b925c5b0014f87c1703b3bf35d9c16f0f7c71e5149fe0e904844dcc70993b118baa3d874a2253ce9c866a502b10bcf33d8e14497226f7abebc2e9c8a89ef6be48c0bd1c26d1fca94b767585ce559e96bab0dfea81a925ce5b9280a0981b073cae2ca88c67976e87af2d741404f22781c1d7f95265d03233b595c515bd43be721346e6792d6c38ca466af5b4f2215c2e2203aaf6a423df4906a8879f37dc15e9aea75f151ad0a5082836e5b73360059e3dae334b5001ef8a6e038019472771a8cc60cd6e61443695f0bc6b0d60f3ba82d8cf12a23e0ab854e44a0cbff9f093fc80ce0cc11031184a79029e91bfe9ae1cc2ead24f8f1e70a0e337049529d23b482e3540a4e9c1c23ab96801fd1cd066705dd6ddf7b5b8dcd4e57741ae4cbe3a1b150ee39cd29926cbfc4ea5df2bbe319773b2fe4f76377fc97140be5b633b5fce1ef91a0284c9f21132b9a0c53ae0db4b7d126f2e1bcb1d446e719c56e699ba6b265bc10e29ec192784605d79cd71a722e7e8df82a452eae778f4d991d1408a53ad9e3e0f6be708de4c9ee2e3fb2dd9695fa9a939a0df25e298abe39b9c74424715232fbf7ded8cf6e4d813a03a06dc5e154bcf944f4cd01dd2b73cb3e0b868bec5e62b9e0556079e605c18be822fe3323e19abf9219c9813560d0b9204a19806996b2970265076f5b33180d6d336d9965ba9898ae3a1ed0d8f2e4d4320b2b1b8ad0ea1a29b01109c8f1b9ecd20721a419b392fc106db8e0367ea05bc48c17bec15ad72a8568e034ce4e1baadf2b07521acbfe81c7c82cf7a9e0e32a05df18faacc38cdedc77989d98234df13ba3337e623aac38c546ed5a0b07e89d81a3d30d92c7986e38b3f030fa4e4b204b43483cf6b9d910d4b70392943102d2d0720edc7a1f855efc00f1791c1af677836f8c81044bf07d2eefda43ddaf65b77deb185789778ba0806e06ab48b767ba6f5e05db6aa8693d15b7ec0a3dcda467b162a22e0873eb375444e11528a13df941e5b980f8a6315ba5975160636060ebebde8564ac1d277a6d9849255af41ab314c9fac63674c4f84b0bc0f2f6fc0ffd849b9394709cfa0cfda40fb18fbb89db2ddb0257146f69f3bae76f5686e85680c4830628f850f56517a4f88ced61586294c1767e2bd8e26025766a38493dd55b6007360c62a9cc4fb2284c23816ee1eb40896e8a15909c9bbbd72fe07dc70ff5b41b81850fb52ae00c8ee5bcbb1d60f54560af48011e152acae038bb24224f1f562b09363b7e10e488bedacfa23a0b82f6ded946770394c2ae476cec3b7409268b3ca28301aada21d1a05c1ad2e6db7c557695f2504c041603c1ceaa706011a1395d01c003a307dad6a149cffab03c5767077696a69339ae1b446064778e01dbb92b02d85dabbb61ab16a3d0162f83c156ec47eb8f709269aaf159dd9ac461e565780f3854be5fb703ce307830deb24450f1d933a1d7821d87cb640de7c00d89258ba1b4c9770c0a2d4635d9425b87d86434c7e39b5627606c95d8391750e86c72789efb4f39fa4cb802c50fc55d15795b89416040ad0930d6048f992586f68ce1140b055724bfe1f3e5f51cfa1daa7ea1a2348d77e7d3586b43d7e06e838d42ccaef3813bcef41d8f00c1ef0dd709daeaaa1c93491d1a93315035d41b0af60b38dd55624446a302e4cb97d95bb0b5b413ccc9947dd38d4e822f62a0e52efe065468c9e9c72d6dd081fb05a8c477b4f90f865bd0e8a88845b85934b6527070a6cc0e18513a77b015ea82553172c071a3a3314334de1df279b41eb0369525aa515756a173791c6ce31cd798db3534164b85a06163aef67740f845320f292b71cd8067190e55a01aa4c7ef786ff278e98892dfdb3460fc7290f7cf41700408c8c5d280599f803f24bf80a0cefed1b0d62f1d0c9cf716884ec0a02901258531e5f73c73f408717e362109c375f5065868c8f8936c6830ff8def8a027ffeefd22ff8a9446ef2b043fae1ad0a348e72c9c4dc550096c0224c02043f5c15543a162c232364ed373f22ab347f706d3d784960c040f34ce09db0536a1cd9df70303bd986579a5e930e19bffeab500b20cb96423a243d3af40a92045786fe5d00590364390845b6467ddf0164f1c590d5934dc12484ef9d5e2c4823561d6891f3623a078a353975969a3c5bb746736cc527854db36b6e0db858dfb1d42dc75d4773ad860f83b684f76d7d8c2d3e49a9bdf73064a8e4c9d021df607dcefb146bc05e4deb176e934626a2bef8f41f3ee5b0ad6fc0d0132d23192b8ce531f03d6ac656f40a90687430873a11ee64ed82c9b72826767fbee6db3cc0dd1b1caa8843a809bb10503f79a5679e4884205052bf1100ea720e537c2c6908170f5fe1892d1978b80fac4ec003a5c1132d92584da608001de1e6600adfa7d645b184487095052946b772a3b49e58967f73a149ef40491bec0078c38242e0e33b612b373afd53129f50ae30e04114503745aa6d1d33498aa5d51db9e5caba32c1c668c521a3d3748a29c79ac4e76c32129a4788602f6ed4018f783c557db55652174d7840ac13a797eb6e26bf83d7290975c9c9690f285c68dea94edc4dbe40cd12acc5b1083c3e6255afeaa84e72031277a146e661880285c5a1e4a407606f927d4f9da7469ae1a45824abd46d1583660d499206fe997d89cfd7ca2c41af89b623e50c4154c934b56b1d9d274a2e4000f6a865cae95e0a460621c7651f7b041eed7238e4ef8e15637b341b8f6c05377d1d202112a3a1a052dded748b4de0d33942acb28c41e3ee233a30ee82d6ba3718b325a2bee9b9e35a181eed6205ba41a3d002f74220f4701fc072763f094388d3602faea84a4809f6fff8ec3807d5534f8cc9b96ead32cf4923463a2a07afe69d031cf90f2ac25c5e46b31669959b31396a64cd8674562d8dfe7f53110c33f5252fff433853d8331c50efdefc34ddb1ec23adea6d6ec9679b6581dbb53e9463024aca255d5930104a4a1bf6fb3247b153ebdfb1fd8772d37294d1c1516df902912c455dfe7b52e56ad7ea0f40783fd9e6eba1f54a9c32450171eaa082608cc790235087f721182e398fdc6bd62a2d4f092b58c5ffdd0891145ad9ddddbd039605440536063cc35cd3451ec7ad7b315e4b60ed789fb12067d98b34dc2c2dd8afffd0604ce77196a16af6e4cb7d6bcd2f5f3a7edf31ddedf0d5f93e79a5bcb2bebc3f146f11b49a48bbdb95efed7d9b929b7d44db6b3b41ecee906bbfb37bd5a9adc9facf6eafbd0434be15c91eede5d35aeb3f69637ca6f9b0fffae3b2c748c96a65e8d4d57cd427b2bb3facd6f732cb32299ba669c7b727bfea9eef63baf7fe23f3fe3e37bcd3fc28e0af6ada9f9a9bbf762a9f6e7f7ff78786d3cf6eb68b60df7611e4f57dfe5b1f972cf3d74dcb9a8ffcf7a97ecfa3b3dfd1a8875ac6473de36bce4cb0a573b5a7f39ea663e67e2f92dc6e22ec01c1beeafc7b9ddd633f6b796f4d7291947c7bfad9cfd02ecad012d51f976df747b52066ea6bfb997937e867ff98b22c43736bbd92635563587d6632e49921c90b7fee5e479298381077af234f3c0537bc00dcbd8c60c1a5f08cbf9f57b87ca940e6a5829657129c5792d34b85241cf3edd970c9316af254fc95ca8f31c6e825c6dde9cb353445297dc926d378501e6fa37a7bac5334543d2b0e3a78a8f578bb3fddfd068631c644d0d5c0f83d86fdbf1a357ecdeb451e3c7da705a9b13dd8920d7f1dec7e830c07bbb7a258edfe6154bc0bd14d37a53830111c90ca35bff1eea9f1f5b77f5a2c071d3cca838ff12f43023f68c53f14695ffdf65db2e1f5533ad37cbca7f85519ffdb83ddc1e660f76cb8e96f0f7ecffbbeda31eac0adf3d88b3f65c48f111f155f8b9f638f7df475363baf6093d7d08ef21ada718cd24e77a5ffe83f27d5d0977378dfbe4813a51e059ce2fa52cdfb207eedd20def5b91c8fb1b82520bd2214f7dec72abbf3dfedd251bbe3df6403e9a860e7feff1bfb8a9ec2e821c6f22faa97fdfe1c68fdf1564c844014518e7665f8050f499df0f40e33b100d52fd7b9aead82e829cf28dd2af91da5ecdbb917afc9b8e3f14777e86ae9f8aa1513a6b3704bbeae70dde85a8e6f9a7f9b03af3796eb41eeefeaa2dc71efb8e63a033f731c72339cfeeb123b8d16afdab54794e6ccb8ffa0ec46b11e45bfef73b1a739a8fed89f24b2d48c6b77f5a628ef7c47c91067f73ee8e9a2fd7bc0f366dcb35ef865c23d9e0354050501c1cbb8fbf13c1c1dfe3dd81387d6c13ddaf8f1fbfbbb217c18bc935abf75cbc8c24bd90a05421e6cb0f37cacb480e37cacb888b63ee5e46a0f09ec7a4a084fbc44d1ae97c9af3c4827c212de112325d28954b185f3c21018248e030d464f9e0bb0f4c789c1641c99d0b3201724a29edf4a77e3f7e3c24286315490254e4784ab8fb117f7dbe8c1f37d1dc19b69c5b61c7eef6bd7f7f83331c3e7c098743d902de332989e6db095b68bc1c499a3de6c7776e6e9b9d6b6518a401892f3f6a404e30b97ca26942531863f8fc238e6556e7eac29025efab49e55ebd1bf0a97661c890f7d5e310eb1fb51e72e7a7bbe43d240e20e7409143c39c19a4207dcc9dbd8f73bef0a864478c31c64fc1d6bb4ab28c70ce963935f09e529244e8aac0b2d60901e188e3050c272972422021201c71ce3914b79c275cbe8c2f87a09c2a5c7e0671e052ca7d57c88e67719c9258178984568e0a71c2a0573807c071820385d604c70670e9223d3dc1a9f2c50daeda08a9ac6105e8acb44640966dfa4209897cac9f6b84d5fa71001832852f987c9124fa381ea3d5aa15f4f102099fdc3579618477159093dbf285858121649dacf106124c4e61716c946fba10023b65faa2842bd87a6fbab801c3d91c81aa9732aa4992b2e90d165c4a2959981cd9f46689376078075ac20eba15761001f8036a9a9c0e41764d6aae5429dc35a949c2bb5583c45d531a362e4d9a34645c07f007728ffb8c57dee39ad43071d7a4a60bfe4056d3d405932b98b8406a03e55a8134068e862af3c31540bc000ba5322a3853c5162caa98224a97363d5871468c1624e17b6778e899f23922cebcd0818e37cfa499a2872784b0c20629359c41628f6ab8a10b2d46c0800620c87c71835cd33243e9e7b2196ad3d22ae5fe98ee99670ee7ded116fa60af650f8b24cfaae458084f4f4f3c63618fa1bef75e2b25a7dad13478ea223cdcd3221cb842f8fca8dfee2fca20788d23179f4fabb03764c8dd8b852dfcbdfb2e3fc2d650ec463d3d9ce443d0b996fba91d76ee67c6e75e6e22f7711731e23df2298f493e340fcd33f3c83c340f8dbb4b1989936423d538b9664a36eec26c5a57ad9dbbe7ec5abc027179a7e42e33e823fff14b38ddbdf7defb40f7dec7ef4b8dc79d40e067ef23c32121b75bae31923308dcfac1f8b91aadc59e65b32d825077e7e559f2c9f7397584bd99b484852a4a903846c907e29d080e2e3fd65ab99db34e14f009d9f017ff255cf36e40c7a58d1b6ac906b6e0946c8e789c13c287f5c92092ccfaef5d7df88e0c8fdf0344be178e5f3e86dbcbd044f8653c8feef8799238f1f818ed473c7b4c33a9532f351ef6337dc46b381bda77f93b9f6978c4b3dde5d723ee341963db2cd3fd61f6b11fb6bea6f167fa3a2d48d58470003918821ce823ab47f27658cd0746e32bf634f263ba4690f898d582e097daf1cc3ed47cf0fcfdab0589dfa5d382e0c7b0dd03247bfbd9dbeff9d567da71ab69efe4d35ffd10ac9a0fec6b7d1a9a48c63bae691368fc8c775a8f23dea3bbeea8af4f4307d1f96d77fcf5a31624bedc5de7ebde7e281679fc07fed4cfd03e1cc7bfd23234d1f6a98fa17baa6f42b0a5a3b32642edb9f5d4afb56a5856b5cfb23721d36a110f8a8450f710c412eb61eff3b0525f4d647f6e70cef9451983831d0b77bc3b8be3ef3f573068fbf90edaf93cf65ac9fd87cfc7fe0706edbccecfc70183767ebe50f63b3fe78ed6d10eb6f2f3cc07a533ab6db63b7efacff16ee367700bec85f2e8443036420c2812c2d353181cbf13e2fd88098fd82e4188771178dc44d913d51e56f66c77219e7dd5f9fc4f47d342dae9bb43c67f1f080669eedd636f630ed4de3676aec5ddeed957fd62fff1a2415946c217638cb1685e1a24944f5c1a247c0249d22b3f326ac9af15b5e554a7604b5e993f279471abee95fc60112ccc62599d6dc93fefbdf7bdc5e963ad200b813d5e886b3aa5ef16a01f9b5576f7ff56cb02d007fe9c52c6dd0384c7dd13ff88bfb4400e7905b730996870af9f391d5393e071f9b8c68f11f89c1b6673ce0ceafe9ed6e3eda107c0d61bfae1746c4ec7e34e5ccae779f9b866acba7afc5a8189dbafda633cb3873ddd3a3f290fed31dd7fe79db371331eadbdd37e68cff3415630888716e4c8db4135b619dcfb6a3caad6841c793bb01f16a589b4b7cfa3bbf6d8efe8ae7d7c1ddd8f3866b5c75a56351e45de53953f9f55bfcfcf8f5f7635ea27fd68b520d2be16a4feb50f351f367a7decb3caf354f3ec1e20f7b5bfaf5dedb87dec3096f5b975defc9d2158b520f23bfe6e7f3e2b4b12e70ed07ffb1ddd8b1eb744da3f8ed244d9114f69ab12d282c899391daf84ec5e2b90e1f3eb3b1d24789b0681a32050d9d37c6c1a8f5a9fd2973f9ed6c3bd505a91f7c4ef7d0cb35f44396602f645948f7075110f8a84c09fb361df691038fab19b6aa2f83866f3e7dc5773ce46dfe20fb8033516ace4f7dff6dbe77faf5abdd3a1fab73b11569ebab3341ed9db5d1481b8272957ef9d8df959a7606be66ced0c1e193176687cea55f9351a5a7ed578a8acb534249f2123468a67273fea87a28ede7e6a3c34d4a3b6a7db76b03504352d79a6259f13d3f687a26571f8731261ad7a685303d59b5b481921a6ba3fa7d26adf332eb16d92517f7f7bb93bfda8e54f8dc726654649aee92ce57ad09f1cbe84123e94dc6910f5a21339af0db9b366fcfadc9b4f0382c2d3d39311d22743a412583eec63f7fe107c4f04fe22c05f11143832012f2b5c205cc11694da71f7b2a22587db0181e0cf0bb9ab709ccf41e7e5881beebc2421f5cc6dc2e3f17796f7ad24bf430c76acfe494bd16699fe4ec1166ca2824d28259271f806ab1027b4aba7528552c1923d169a604945091517b89429ee5e549caa0c4e53aa58c2440e5f947ce9c2490919485c596ac1873260cc91810c97329c09c09754f6a2d2431356cd1529a248b286cc0f49ec50e2c2030b9ac264814404975262245476804d3c38180421151cf873c18b37f1f7eebd9dd2c51c1cd4849903a70c1c220c162f9c746812831fe4c4892314e7c929534c315285a84cf0ed0b126078c17e3862d21fe42c65997303080259829728021b06893644bcc45861830bcc51620631392c4183146078c1943ec091c3e578c0756042e0ef4d5ee5942aa754b5aaf2cdf6d229a39bdc71e0604056bda6bc3025495e015f89e18932588028828797933636082c4a70b1030c88886287a74d0a3447586498a208261f5e5a84b14485401c5949428907276ce003941ab02388784991f28a62c34bca1016fe20c7bea4e820454b66254b6a53e02605475e516488214cb9336945863e3bdcbda224e192bb57942478053b16ec7102121292110e9f0dde7b2fc21fa2045d942871b22209ca94283b5d0000e58526ca48f96189098be3c5cad31256bcd6bcb2a09204e5c39114b00041840c615690038d329c4e31847c4181f38292e415031315c6a08a570c5ef80cee5e31d8f05e64844a107256d6d7939793248c7072051059d6945973c4eb091828088313ba3072840e3168e302333711217e69f07a228653ee5e4fc0bcee98f728dcdf1e6b149686d4df1e05a8efcb1f725fc2bf8fed21dac76df419dc39479ffaa8df1ec3192f23ff8ccf6fb43424c6c37fef513fe43d0af5efb13d44f5db36da5eb5856ca3a52132fe6d23a0e8535fc6cfd832b6d150f4a9ffb68bf533dc461f7deac7d8f5a5be1f8d9686bc9e09c8bf3f44626f845f3eb687c4d7b6d163127dfe6dbc8d4670ef365a1a721f6ea387b499c07fdb086e2324a3a521f8df361a21fad4c76f94bd0e50ca91e1e90f29f56e47fdcd497ef587a2b4da684908cfcebfa351c07f5f47a3b03404ef7725fad4bf8fc295cd49dea8fd98a24fadbf45c91b05a3250ab81df36e14e6bb1dfd31719496bfe91d18fe7352c50913c70e3ab865e911429f026e07fd0a3cc7aaa8a308ab0e2c961352b26e134fb0ae18312c31424eced13b23e3310aa057b19a6dad2f273290609660e52c4bad5e4c94708cbb17131ab228af26555e4cb65c29e4bcd045922c945c26a0c8dd0d89605b1a772f1868008312bc037d9144124e88b102b9dc1940a50e8411a50c11365011e2cc0b5c685c63ba50a1c29726dc03aa8dd354817473f50bd84283145e3f5801840b2078d2440b522c41451660b0682d48f12034031c4bcc1401859c326cc81461250a298ebcc0c5b4e6852be5ee202491b87cc180c4be406806d6891a9448f225c9ede294edab563a28a14bd3862449039218abc4b082122e2451a97000d934bc0339d919c0c4b7735e60f302920b5ba4f07241cd1125e86b491b9e71f772810cefac1c2e12af2557d4e02a2103272f6e7813440a35d8d1f31233900b0a59a05923064e1625645e4bc6bc968c711206bdfaea9efc391d4e06615455a79d55e33741e4fd045e14b95571fb58f21bb9842064efdec5df2e460db9844177877c020923c8f4e3b74aee0e0d8bc727527308916e4c064680418e53f9f147504f1822e4693c28f6ea63954aa4271fc76f31be2ac81863fc78b364c89de503a5ebf91d97c7f4ccd05404218290e477c00701be7c9594199fdae197510a356df7f44c203eea2b007ffbf8a8adbd0af0b77dc433ad35ddf1538ddd22c9314be985f28a2c89545b762d556103f871f742842e95ecac0624873a02b09565186fee6db085b3d732fddaddddcbb3aef6f53e26b4f7431042b845c0810130c79372e50e91dd77176485e4db55f29bf17bff69793d3c2d3107eaa183166a5b95f1c0f2ef2e929c4a0ee53179ac1f9d0c5b42649fe7c5ce2121bbd71230af25af253aa0b87b2da90103af1698e0eec5c2198e00ee5e2ce4c04920f22c74b0f5662a93b78639b1e4ae9a1918e17df0f8fc22262e1fbeb083117bc971c020faeffe391df9dd6ff659ad1fa0ab231663844092ebe816c6285d3394a65c9b3cebe76c6c7a5e8d3da6f1d0304d4b7ef7d580bcf912a52547e9ae9de7c115640be06830840b014a515cce8b1cb2b816a0f4840dcf87194a4fc95101c39b3783ab8205635c35706a5c32c04e065abe6832459217316c965af0814d1527ba008184931d2e34900b1bb850fad2863e33cecb93c23d3d29685cee0b1664e4b8d2b87894cab812a0a4c283b3828b220b15346103e5c9962b6f092e2e8c070512ae2bb809a820c2c91a1fa84c0186833269c634dd70650a1333c400db64f9514ac30025b7e64d7161d0b8de11c7c4936b00399c1b178070a59468b84113ae23a6b800c3962162584a630689315aecc04217705c5f40365bc4b8a8d21c08022376a0018c2fae2e4e6451260e174f5b507144163364e1196ea1454c0c5e4e3124e1c5a5b429bd246fc9c54411aef7841b2f5c800394232e34f1363eac8112c697252a765c5102d768794d8e0c162aa5f702f784164c350c01941e962f1c1241dcf0a2704d48c2b0c225c70586532d5eb8a6d212ae38ae96019484905db87046e8c0c0e5ba7035e0a5f0c0e850725a1c40c941f190c04ce17247b81e0e675c2b194a6e5c0250e2a01291c482244338296144c973e1c9d29b373cc8a00a17159c422db434e1e274b1c593c165b1c39b0245c504c3f57275a064c6f5b4b06cb08f98734ec9e79c73eaac2e553573baaaf0a9c3679a8f5f6a28f1411258152eb7854b2f7181a0d908416be231694b38e79c73ce39e79c73ceb9b723364919638c31c618a9e456f479100ac987f86184706231d6c7a2aecec655b15e159db804a19e958cc23de4bee2f36ef7e34ec6309d8a7dd5dd6e2cc31e376513d43844c62918d41fd2576b9f220591e7b7e2fbfa16078f597c9edde92eaadcee8a7754abcfa8bb69ae85304768c1e7938f4ac63ef532beb7788ccf3e7e049c8efbf15bf561c4f145b0d53e8e41ee8fe931691f612faa5c04d87a0ff8bc62391b52c810d87a3f43cb88a15394d60a61851042086155adba64b8b17b0ee60d97c16ece9c31ca00093e34816dcc91b454a993d32bc71028c27b0d65b32934b4f14c6baa156cd10c95657bba6dd510742203b045c665afbdbb23703a8c9c8df78e099981ec5758d396524a89ea6f5070fb5d846d86caf2c478177374807afa0ef52ad5ee2b6e67eccee2f63bd036c6e6d1d90eb6b22e7957f56d67d916025bef573dfa2f5643d3d00f593ce40e54e1d3f724c0d69b01e9131863a791e152522a04c485c09684c239e79c24a1a8f2e88ee88b582c13da94bb85c3bbea0715f6d604998273c20b4b1324fe74ea42266184de4d8c3f0a591dab50c7c1e9ffc01c3a4f6950f6f7e9b360107efa2d186482c989aea53a76f79f4cab340bb6ec676da18ffd11387ea9fbdfcf347ebc5b52dffdb0651fa5ed0b71bb6b8d3bcb918aecef9d59b6290c83bbbd7d48448867d8c26f37fc4d67b0851f42db2d67d1bd334904c5f8e79c73ce39a7c5554f8d8677078703912facd9430cefaafb8c4d3007abecded19e1af63a1eef753e3f3c5e67e3070790d00bb9ff5c1eff07088710036864f80fba00f7785c95e3c7c08ee5dc9e91939632a80027f975c15403f25e9d1546f8043b77751ef0e58a1214fe385e92100bfea915097cf27ba7dd4b6909872f8fe07b3ee020f9bdc3f8bd6349e1ee55450aef28ee1e07772f2a67781119d9858063e4a3148bc7003b3a439c753b1b00b569bb43097196ea9ccb32fc1eb9ad2afaacf06530ce4ee29b9396607c10e03b98e3c18de5f7431005ef2d00dbec24a67408d24fd127b967190c8a94e2286594b1ee9ae7cb7fee006024cb2b1e5d64c156fc872d0c25f7d50a063dabf503e17b926e7a038c93e4997b3c2e7570cc4ed67f5a36a9962dc83dcbf27cd5d326bc1877160cbf8c6a0d621be6a4a5b7d7dea4a507eb432cf0cb1bce494b162a007ed2920bc2ce04e9e6773bbf4b20b556b9b7c530e4f771f20c8380c8ef6fca5ceb4f54adf3eb840ff223fa1e143205836236e5dbda134ae91f594bbfce497fba679225ccde0df83c30930c33ccca49ff7dcc5c2276248018a5b9fbf7f8db13488f23d8433e840f816c80cb8f1a6a3f34ca4508b79fda8fc7630bf8037922814e7b4029c98a1bee5e56cef01e02876f00ee0294c3a8548d969541ca94ba91048000d314000020100a874442a158289668b2b40f14800a8ea6406042964823490aa320886110846110060000000010038831c84063a9073327395d9b9870d561fe74b46d220e87ab2614418203a17794dd45c327323245e304c52c755a47c87487fd76185289167c644430cd44995dc35f00cd61ae1c65f768f82081e2e0d6a1eb58448e3e70587400718d24098b8fabd338945371c0c7cd5ea5e7eb42e218358eeaae4102a73124e0b4baf1e4c6fa162e8d11706928a163348c9952c0d02147d4a7ed2fc0b5a74a2c8788bb683b7e21d370f09b83821cc571d87f07c925dafe88e00eb78ed3772464711a251422d4d1c44946a6d038b194a596160e32620a0db15c8703aa834c39dc1df73b1277f8ef50762cd6f1dfa1dca1b81c7c1d94e6209263ff3b8a50a2e52f0dea706dde98d669403e4f9352093eed3cc0e95432d2440352ac9cc31050b4ca114638c88603dd3b48eed17a2183e2f8d70ed3ead1f808e01a13921d87e21deceb30e4529af7c948101a9322bb438a3b48bb5f44b22f70d633e2c99838f6d90baa43ee8a537f5482f39ba149fdc18bca329277e39d2747b940c9361999e48dbd2c19524e4b4d644493167441e280aee6ffabbeaa4efb2f20fdaf0151d875cc7478e9a0ef40bcc3fc1d24cb68dc2f4300a78529292b0b0d7f126c8e73395f1de8c92921bde3ed9f462069fb7cbf00965206548ae8dd147bf59914128d1a1008b20e83e228db85c60929250e7e1d4ab5bd1238875707e8721029a64134a9ea8002874cc7978e3a702892c3bfe3a2e38b7790dccf1021b5e2b23829a3d1509a09847078d762a609fcd97bdf65299b9ca61ac8c8262d200b4292d170434151262d24a112c7c828b495c531874cc7d78e3b398872d8bfc3904a697993201d5e1c74e240544328d9350711686959908ec751f645036a920c0ed01dc6d141924ec30ab138671afb1919a1630744cea4392586c5315284c6894522bf7611ebf7231cd2761a0d22693535d0f020d2902043e3b263492038de843abb9e18434da5024e0eee1dba8ec5e3e88fc36207716da2c815077a87190e7677c50ff516124d9a872e44ca68b8918c6cd02e54917a32da860806079d0e44387c1dbb5473c4fbc7a70969e723a12535bfede8a350b7a593dd0cfbccd3c5d80ea24b91898fe75aa032d9eec752153f35a133fe0080ba20a351953fffd155955846bf4a4e415462fb1bacbfe256e8811a0d09db8e590eaf1a100589c3a07594eda2e14384c8295a0404d56c84e401211c36ebfedf94b3d6c4718848fb4c4871f871ec526d049f08ba6d50887a7a44944f2451479a91f360531a93fa3fddd9db78378b76530316999f2c41c910a7cd250c42c19ed92ccecce287a3f0f8f9096c7b7658c711f57972401aa02a5fba3a756fdd80c51e80a587b47abdde29c21543fd14725191c58a9da39041b05043b742f989465deac198674975b5b998615464f7dc382301f4936d45ea4218d6cbe3b0f038b195db99e6d5e88872fa0d9e98582d604626f847a7748098c57fa0a2ee611ec471b4f51317ef4791d1da7875495b9f5117b6c322c21aa7c7d111e607721fe8bbe67092bfe20518a4de25c343ab908519f96e3b7a988025737760b0f18b8f47f082bc19317a34e1b91660b846111d484eec0fc41ef02ee727251b23896e5bd682fb24258152f2eb50502d2d07c812bf982c4602124b316718c08ee2abc37e70b891e88ce7e82f7e3d06a73eba5a57f35b3ec5661dcbf88f086aec1d1634a275a9256b179b8bc5b61bb7675724b3554498a072ad307729bc55099cd45ee4c25b9a35e00e43bd90a8125dcec3919f2e1e7fffa0cb4dd03367205d42fb1776df5002c1121fde081a7900607801e8fa177fb4b11270055a8c8125631a1b41ba73d841c89b499575dd53dccc68aad2c8bc942dcffd25d3e2d2c896bc1d0af27921c57bedbdeaf970251b16a53914f03eb8841750eb6ff1c53d9c3890501224c5ec120bf45113d7108256ece5f6c9a6861cf20d1def0c4d5bb871b2540d29b3c7e5dda5233f91868f447864ac7369494209800909023e3600953a1a1ecd36846fc20b39504c9f46b417eb94b393a6651a9f94e4d57898c95f005183f0be05d28a5bf211491196a159e23d5b6399f0789f3234f3fca186befaa2511a8ecf62f1aae68b8ca9540b9d5604640c99f3dcdae41a962d2dcdd8c5e256cea87b56f0b4ccf111bcb63c1559971bd4a2afc0a0a3d6066bdf9fa42092013c03a7f09a15c3acd7ea74bc8030fbaf050f9f5fd066a4442e6e60466288572841269f6e7f7a8a4f4b5823159b4c26bb6f264f92fd91fff0ef2780b85fa3a1dd237bd2563f550707f18fe9252055fbeb52c9a7ddcfe73be56f005a30c5793a3d7f86f28f5593a3e522e0762145f945bace152b89d11a16c20c58e29baf3a9442c59ba0e6e60f77d23511e0c8e0344442127c8c76ded0c216b1616d3607ae4752cf3d816eeb21ed84111c5b113f79abf961c52641cbf6be7c58c5b0676d9dc256f3f306485c9f8b81cbe318acfdef1a6d7de49065384a87674f1c8114eb907ab4a858d0a74675950eba0504553080d1e16628a46299379b7e34515b019f7861c17ff63202446d8fa81358e3200fb7b132151938b604670b81a375710544a1aaa9266dff23e52c1d9d0a3fd30719ccba5eb03b7d163d6c0ebbfd9152fca188c28cc949f4bf2d868ec9f2660d385b8f649ae8b5c259cdb4d846a6333875d41cee1215e62be3bbe1beb90ae907390665d952038fd03cc476419dd8ecd2f41d1b1c7b4e99833cc9fc1e8f55efa67d4ec6f9bb1283b4c5c1d6b280ab894d480d02f55a43773fb1efbae110b6e399e566041b79313b645966626b938904b2d381ad5a7597c7183ce002fc0adcc2a0bb172d9942ef3f606e40ff6474c5632b07711c8daafee32783b4173ec6784c0c8d60de22e46966a82e9010e7faa5f9bdce1083490574cd370680920694af2a39e4f5865083ff6b61ee0f6675c9b2aa9d92372712b4ff20767cd420166e97f1c1cfe8a1562ef543e4b80d6903eb60c9a31091822c90163ec65f2f0e82c18e884cc6036c3964a0231aa900c8bb3984956a375a75d65469ceff6b8228d59ed18ad400a6a3b255482464f7a85b91c7830d4604578c8d8f88c85bb318d6bf941176e08a83aa6f9ad85cc0d1046cb1ccc3967b4cbeeb5268bfa03f1993cde574416e2e2a07e9170a05e29517df04b5ea90a8cbcea1a612a2265383d4db59b611e48063de94dd81ea25fee2a19d9a4b80b1b1a2146ec973fd73e8b01cd54e3235a4b2623dcfb8bdd995a921456ac95acfa78c0b0c8184d9fdf6b43c1acb18ba2fb6593b3a1b0da92fbe0794f8e8645f0a26c64fc50fbae2dd8b809bae5cc44450a16f2da44bd672a8214b22786f7679ff232b515bd46ca64ca1c1c72e994e852ee9ace5e48c86a4bad187983e08cd08b61a1513a44724a1156c041b0485285a0e7fe0b231a9e5c3769022c055a50bb1ce15e9a57200a8852652e8afcd73212ad2be9e150265c8c951d27ff7ccf8d5925742304cd12ff2cb24720e588950e00efd000c13c1205d2ccfc13c56252d20c34c10d407dc54b157914cd4c0a416a824108438e613b289b840169f3ba49933d9f9f0f807a3e0c23fc6a784d43a7fdc3ad2a85e7364c36c37b9225613a39cdb5e1a7e47f773edb1fe0120e139eb31504a701394c3e87859328230333c1b1d7a17a207473fdea24a3ee6932d4455ec177906b5cd1caf6b1eaa5e0b505fa9085c344e2658ad9bc071ab7d83900154b462c7b05fd67aea0d0257dafb2b76f4bfbd46c632e71f387c342a63eb7e6c7181a97b257aa876cf8bab58ad1d999c45d1bc5d2e3c9fc5bd4b3a79c948af895b067dd9dd324cf69f729bed3be12646e684c2832719ecce363e158ed1e12b4685db0018a40af79e2c25a584b2b0504f075edb4f089b8bc657130205c6b0a8643175d7ffad036ff566fd78282225e0c61499d86fe4c1816ecc7410193dbf681b431f2dc2c8bfb5b8e17b9b126e39f6a7e92892afaf39d0203ba594e8318e47fe51d1f9df9e5fb9be91f1612f2f65abc5149f5dcaeb5f6bad4b6ed393f37cd01b112ba73b1570fd6b3dd36ee24702341bff301c4605cd3b5348e4baaf918f9320e1163f9f18998374fb7aa6e025d688710a50a962b4dbe313ae042844f0b51c562d36fc3bcc6a8dce8cae6665a341a8c64ad2033b07d0356a54868501d4407989b498299b6994f0bf330d9557941bd9dfb00c91bd3e8372412f92bf695152a200a1dd9c3e5a881335153da65a78435ee300e12e0e1166a04d316c2287842722af1688be6b01ec89a1c6874ea3db7294a8531e0296471701784bcdee3f56ac37693b1a16e1a5e053e14268dce69a02064e6ad10c3a959377381aab6aa95b2194a171f5d6499e3a2fcd30ab1467184fb759277d0575664854d672a3d48823089ce69306d793231599839480d8b1010b587dc3d4101ab75738d795e119b2db18f3e935ca5380954b7349e4eff0991595ac38bfe607381c399de9a73004a93fe0439065ac276cf04786b9f2e1c51356d4d228811b6a8da0afa2b5318504b520dbe503b8aaca895c2279e15af9ff4e77502b5b22c390d59c6b428111084b0a5f0cc9d4b3a20653bcdd12f4c6da2970016ec29b865ea39fce79a0a2455df4abbefe04d2d919cf91c31b4733d4aa655530b4cccf3b25a5a0e08f0c12639318ec44ea991485bb610cc808e6bad7137de05d55a5809c251356fd973a6f000ad2912e00e175e4e4a7256bfda9f914d5415429bc481b8dbab8ed69317e2ce14aba1ef2101458c9fcfff2c2f6d34de1731aca47be5e387bd01f1b953f330272b2dae411a00fe526ebd0a49f25c7fd3c8e3e35c02953a3ebbd2df62228bd30c1c34f2be2b11c78f7c219e78eba142b9d1fb273f834b79c41992dd2cbfe0fb3d2c2bd2e4865a8f758457ecd636f43dc1a17dddd1556e119bdd3f873251d514a5e85bd6f9068c2a8a4115fbcd7350ba214f41ffc7025b5645dd6fd878369ea1c18afc5519b710c931d53ec6d1efb2e507ff8ac69ec507745c90994b9cd001df96dafc969a1e97920fe0fbeecdc0a484e19aa73ff00044d91b15958c55b99e5400b02ea673d9022ee7e588d87962e60ec06b5976554ff7d569b5a2681ee9b47cc1359fe18e8ec218c6ad11997270683568bda050fc3ce603cb26aca2c1605786918356dcb9527d0d948d15258a56d6ac3b89ebd6e27679a3472cf2a5cf349c698f5d6ada7e0c5014d7ddf0a383485622db4f851050ca6d040f5cc6d0187ee623543bf74e5c526f678105058eb1d8c3807bbaa50d5a4d5399fc3f6321625981bcc0016e1be06d004a60c92e18372e1390455b1358a43692e9abb5d378311866a59cc4216b76f7230a5608c7b5d5ed80aa433cc94ce88c588651d975405f7f9b43e67c8840bb9bcfce0e0f823274ecc8d323cf43b1c1d9899baedba4bdabf5af4e50b23075d54a40fa2dad9de07487a1f2956d86da9c9e4258001946970d0727b701d1e726804dd94335a6e23ba0d004224a2f4702de2d1706390a2a0ec71520539f579a92a03e6a3265d96b0f753dcf02c1367d692d3808128a13270e2888efda26b192a699a7c78dac39176542712a511290974bfbb38fd1deeb48b526602f1528edcc822eb45ac0c37901a053c402a65ab9bd8077e2a1f851fad6d289e062663acdd1e118f8c412b144bc8e8a5f71a81a4acded4d50be914a9ae83a6dece79ea3fd74955936a9d0087a0609f2830f3a342a8cb27a1a596162c52ac302642be384f67edc81809b98b5d9549464228c169eb8299062e49548ab0af286a556c213b00e4fa8ee558a3b24755a6d8eb38b8c12b35288121baf9b192dd1a6c9f1448567f13e94e8c6fe433ac05637c0c263e4ab87bdef329092ab3c6040ad554f6cb40d0d83fd77a6cff7cf79404c48fc511f7f1732daebd75d737060a679327cb160ff204cbdc204d89ae142a3f27e74a59865b4458b4418eb943691368295a0f87290944584a28b598cf6a90a48fa4ed8f33d6d928f44789d978cf23e6423e373136118499185fddf9c3de2e14c894877f47b1e3ef4986136b58dd44d02902f53635c103c5899f52acfb4096af4eab5076ed93c30ef537bd1bf1d8325830676e6ab440216344fc4f4c17e83e48b0a63ed0cf38878cd1192aa0d4175507b8a29111c26f98701d2e93b1c47ceaac5c7886a4f139a7ad31bb57c3ce40c544bc370e7cea4e80eabc2e3b7dd52fdadad997b9ca53086e61a71e363af1d66ff80271dee61a6f2d18f6c2dbf21d21943e88cf43190793978103e6dee5fec0e0c434975408649b6743bec970176c3196f6f75eae6dc232c5d3bd5d3e8741f8eea0aa1a49202287413e4fa92938684989095c8e6fec7a658621a6501264903803bd8c97bccfe63e72251a513c4c48c0000133d09a8a886c7cca39a4cc306b0e757e0107e899c1b7c3274018cc47f3bde8d982f4dbf4158a3dfbfbceb834ffc0f232457d783ae4f3a83ca12f20dc0c58786dddde29082699d71ff81a3d3e706f5e03d9453ef36f211fedc88d17769926abec54c5a71a437ae2a0b688ed3163ccf16999353efa1a2f0347591df28fe3210268caf6a1442898e20e062e16b566cf48320c685b68eb8531065c705dee2b020946db98f1106daa478889c106a5666690108a66d136a481da674b89a32068137c51cc10687991aba25a42814b17695c52e11b81f38463e77acaefe1dee56ec843954dd7b787149d8e6f384b41f3e2a3ba3d415995f94600018a5fa12626d0749bf13d2c6102f949534e00313b185e598b0497b4ec4c1e068d0e0e3cd4c607daadd80c76cd7ece93cf3f18ee0325f453d051881f813158c14f33a6fd22f7321c83fe374ab368adea238b7e0bfb8468bc07639dc0c9a2ef54937738316d36b1592bc1ffedc0522baf65da33b1c3141b52c27dcaf5d3eadb8fd79e9512684582e0c9f2f34a35963b74a9927668e794651a5dab45a1558431b7bee686a15199f80c8b100523903edf350db9f4965bebffcb2a0d77ff39cce17003e3ca7a4f92e379b7530ec7fb26dbba644d451105f60a20e45903f3c974f09cad4a848a9c2442f9e54474f31907cb4e4a17792a3e3278042ccdd1291a56f95ba58ddec540f7501c5b36db804e9fec7642b86944cdf26c337a8e3068ea4b0714f230e331ad72989c3df00e0dc45f230d10f44b7f1a8ddbc6aafee4803f73ea26cedce17c630a0039fdbb5d9a8675f7926ed105b71d468eee0c13f03c77bbaceeabd3371cb9021a6f0ed94b2c1e11aed8c79143789187a7619d32ffa9ff1c15990cb577968eaa95b2f62113cf8e37a13ad2d9cd338705bae0197fc46034214f96a1707f0d3564ec93450442391ab2376d615c1ebd28a8aac79a1c089a4b0c5ebcbccd16b260b869c0decc447c147093f284e6a3f98af06fb5cd4b1fa593a6c8c866783ad08b73dc64b05f076f2c837fafc7fff5449e7c97f274d18d41fd23bf1a1cdec87c398740c0dba8730d8cd2f55f6de7b2239a60106403713bf328d8250832c2eac6603a647a523815819e1d0b2df56fb2237d34740d849a6771c0ffe991d5a20835afff1663143486e8bda65ac9f3ba40001878b9ecbf0805201f7dcea557fd6ab31bf26a696584070075d2d2b3790b8f1eca88a47b4a4a0b54cdd395ca1d0eefb921aee94dbd7fa57cff18c8702ee855191c77d2fc200ac5bbfac28d84b153187d7e6880d6a0cf6c96be7881386d23810de3fa57f6a780889226fee0f137ca83d85120264260c05dde60f17020f7a3bbfa429ea453f9ebbaed4d8adae1ff90d2eebcd5db8ba0380c0746c50cd8303a33b6bc39c32bb1cf734d78ae6de8eca67bdf4a17a86dd916034a5982827dc138a84e1cbcbe48de4e5c50fc8945756544eff1dcabd5731ec274dc3acc04b890e89d5068125e172449395795c65c3f76cfadcf8b511a24a2caf3f34c4e1ce99647d57f5da944b8c6f53e0eb9d035d35217e7f223ff73e99984828e0dd93ff6d55611be3842eaf1d7bc7eb981aa3710f3c05fa3477af9fd947d5ad1c020916cd4f201519233891c0f3db6afd861474e84e4276a5cee244206dace146cd8bb6b2de892f24a77a0ddd5686959293563500d4936fedbc13e817018b464db2c3389c7a79acb6206c240d71b7f4dd36358783ead3dfc4397c1dac704c316ff885df1c45832ea19e2bcccc8cd5169f3a65790918ceed3923345395a033482122ef14de5b859bd569fe1e9c868bcb87b00f3502090f841a2f8da07d0305c7f8d831c1d0987af6d53f7211b3f47042962e459f61f696578cdd6f9cf02bfd39f1fbe72d642ef6a95df422620f9db05bd0c48d46b3e9c5917f99dfbc2b553dd29ebac7bb9c9521814c3ea9c9084064382d186cec129576eaef6a5526c6f9d71dfe4f784d9980e7e3c107ca669ef23f50854d9ae6b09d3329e63fc7b5fc3563126c47c5f409fadbd2ecb486858082955fd8a3d1e98ace6373e9c9ffb93b3fcbc3fd6690a84fb27340cca2171743bb939075883c927e3194b6607df587e6b7a471b36b1fcdceee38431916be1ee9476bd618fd0ce676e48982bd2dde23ddc7d3f553949a49349c45dc905abd9e6024ccd00a9579e43e983dbc7d0cf41ac6f7e20af0779fe7448aa5ddea3fa4276dc672d0bac0a6dfd8a815f1118a9800fa942f8fdfd8a4431e7e4726e77f7bd4903a634d3cdc630751802c66abdb767c1a585a504e5dc2d61c502ab88d9727970d641737c0933964f3af1299554a1a39704888492ccc873de1fa1fc1ead648dd73e5cb59f06c19255f7fa324dd737157b879d09253dfb9f7083b7722e4456e2e3176b1d7aa7b924f49c9b4b6c8c95e16a1658f298ce9d6978bfd9fb6bda42dfa5e446d5c17456eab90913a1ab3dbebb87cb7f142ec3e585dcd3929c05c2a018945978eb8fe3a98db4e8494c2cd3def326c795f5c8c1bee13ea2fb25483376d277e960293126b66e62ac2c4e24df700b02ba4a01bf3b78bd3752df786dfe8919710153037673e481e09355d01406190365d4eefd115aceaeb27a65f0bec839d96212a49b237ff8323f610783d29f1810161aaef781bd71eb3a88dfd1a36a3bea13a687bee50a44226f747fc3360f7049176ef66f688095394e65fd656f70455366a051dfc14d762a9878ae94f3ed51315a345e342e0d77d7e08abce61e5aa0aaf06d7bd0ceb38075a6ab10ea7447b69b9157be8e88b8da4cb3ea4014895a6e5aa14f23d3dbc2ea6110b90cb2d98987693b9fcb3066ebae0fa56491c3e68a71c1f1800802485c2774c7e0cc2e279974fcccfc0ed7ee52f26bd3f89bb49b81f53334cca06fd67b513dc41da1afd14279a1d0e25344712c6f45de11c22f0d5b80956fbd8179c17a57f34e9b7a4cb450540bc036a9c732536d7a0829c0961d98f9b22c9691efd3be4aea536f2a5f7657aced60fa1eda5915672b0595c256465c8d30ec32798e160337704370004c3083ea42013908c028abd9e2d158332dcc80653266d2976ea95de4a2f1df471767c02e42d5d4ded9a54eef740af751cec70879f4814a123995e898a5d32bdae42267e77ed5628d6b4eba13e262416931be57080cc3739392fac9e1deea6651609527426b08b3ac1517f112e1952ee0311b415df195972169325e9c22a4811b345470b2def95d7e4f249666024d37ae20c786642ce4e1397eb19bcde6e8585b5a5dca34778e30b8e5c16e93f3ce2e42c2f6e7c6b772ea5fe3216a80d8aa5da79ef4afc2ef426091f39745c64509a4c298acb37dba9673520afb5ba293a1196c1aa7cb6e821ef251fde8feaf1f350cd6054215f347e9a682e3eeef7fbaf21f388fe752f0a45bef5d9bc10e104d44b8f23c008d856ff0dacc110c4baca035b9608b819d85c8395511344cac3aa406ef1f971d518046deb9a7804dab8b329f9b4e4d5d5f474612c351af5096270f334245cb40979f67059a3540ffb8c8eb4b98efd548d476c6ef7137891fdae27acc24ab74be38226d499abda1ac712bf427431c6556eefe4d996dc1726f5c9977300f8aa8dc91147f0d094d0c940be75bfd220b09d76e28b594f5048c0ef5b07897223007cd569a3a4193891f8ce494e1ed58270a67f42fc2400a52f68a6b2886f20f43f626a94f7c7538cd02c1f9efe67b651bab36287444d21fd21c02e9bf43db133ff7bb7099684694fa2cc2f429d6c6f23a8c0d83b511c8f0c55909334ac3e9c929ce8a8515880aef9c452761bee26a42156dd86b6ffcb8227b35d9c611dbc5694bb302da9cc8fc91fd8684f46d5655c05d11a1fc1d3534259a05e72d1b7dba04502903e9c48ee818e19ef63a2ba3847d13494d21577af2f60895f070846191f4a7fcfe7423e5486f6e706a3eceeb35aa5d8fac2fb2e6c30a9a6e4ac03bc49fdc60c65971d516f2795106d0c2a12cb31021ea13f87276119c312c04eb660e7c57091195d7300f8a66d7348a2c54a0c9c8f6e99d23a767199cf6fc9ae678fee2b0137fd86fdffc557f13784fb6b15510158b39a9901373ffe77f2a3536310afa4ba5d7fff76fab9498fb9b36dee51e8318fd4e0f81fa947779dfd4a9d6be233048282baeffbce58f2b20879824237934155706981910bf0edb9ceca8f537b5e11c55536150f180f86d16b2f4cfb9035042a653ae2dc04549769fb954e711f61acc1ef3fa79e5ac06aa05fe277bac04577edf779017a8debe338a3ba1888108c12eb531428ec6484c497d4cd9f299e75b3881f641a62c9569b32e6551d799e01470bf2a3531d3570cd07ac2bf5932ce5883352a12f362c42384104a4b5586bef626e4bf994ba35140c80210d70db08e2b80a278fef724fa9fa682fdd84d7e658c0b7c86b5e41f9a95228f6e20d93d689a6f5e2678abbacf8b2a79126b6e4fb1e6987540c96cb80b544fc8695b7033290b18c3e4cf8cc90b2e9ba6328d59efa0615b5543fd17c7166d91c17d296dcde92f49e7ee2e7280434a21a38d32eb33025a95a5a6d0a1f5ec1a2cc67db822c0e3ba2657eb55b06008849dad784ff2ffa8fc65719679d9c0ebfc1d94fa2de4eedc70ff475938f05a979c05e8ddf02da2faf72517fbfe9ad62561f693b27b392cb1abb111aa54e37fb401c987b646daa83e573e9bcb3bc500ce260b04e602f1b395661b96fe0e7d2c99a9784fbe4213f9912a5aa201d556311f5cb1b5d5f1b8a523ffdbbec59a28bd2a7d56d36edfd6c7046d57c0295fa55c477735dca717f550b8de4d272c7b3b600fa22eb96f9dfdb6db72308f07557027acf0f9ff82d74f9aefe298d6120c92a997bf53bc4d62fc807e7605b1bb2834e090827c0851a787150fdfb2af98f980f1284a9f04da4e1287fb75a0e6052a600fe93fdb2d33af6676714fc33aea689a687d4c22fc06e3a310df7d4e36a022dc91a7b580fae50e43d9ed7717bef268208c36793c5cc7794a145fd877282ad64645b235e753e41fa3e7729c450da39a3a9e2131854e6a7d1919bfc83f1486e17eb8aeb73240ca5b8cdf402a384f6a0978b9b34d50b8af1f43c86c27a215560bc6b6f60d2b725277d22d2d8ed8e1c725bc2919e170a5958b5ff81b4b43a392e98989391924e086a2261fbca7c578f0174093b85c34d548e9299881685fa64bf0ae9966977389ac513e0fa5ce429c97d46608ba1707ee3e091931714d3c0c79d80b74feffd5fd2aee5c52a3cdb1ae620b518ef5979927de3f843fec4c6e5f57d46fc94a2773d65f1894cbfe06967cd06a7d35557440cc7d2d487702c86d187d24b3f343dbf0a51fd6346ba0bc07a41390c37f60f4b18c484301e64c52ede9c2f964f577be9dd29e77fc9b6a82da3b20d7e24b19cd00308e0e0ab7c57f7acf68418ac1c3a59ae975c2c2d7222a3e554452c7669dd360b00af84d119ea66bd8154f893fb24a7ac4ebbb943c625ca55320a96fd5651f43fc6681d616f741d3a404eb7ec0c502bfaecb66d09f81e718f0bd6853600928ffa9ba68bd580aa6875ad5ef4efd44595423fa7a2beda3bafaf74fb3c40cf8472fa052e7c6ddb497ee084b041f3f5b2b322813f330134e4fb15bb9e3364518f192766999dc3b513dae779ec798e23b90d9245d07752f66625a8db1c89096f4ffa2819b27f0e85872087e44b23cee9b1d5eb6be1b46129e8ab8775065911d7b0f0241646e9fbec3f8a214776f57d264da780a02a141512cac8e2973381eec3f07f719efc3e6eabb5b6de04bc70dc5aeeee2f4c4923461fee3d6df19ed706955710e61e2d14d43dbfd4a4d6eab4a44699c82aebb3edb5f3b0c670ecf0e9a7b46627c0c3b106d9853ad3ede99be419ffd6c4bd45a191cfa5dd7022d6bfee3d4421ad51e1d84a6fb521ff9b61e2cb0cedac8b13afd129663ca83ba827be1c3edccbbca5c0eaf28aed7095f24d59d7d43ec851227a4203bda29e10acdd6ff2e0d24e693b4dcacbbbd81d69f6319d8f557dc01c48784d4375f232a571c3bfb44a8b8fd74798e9dfdd88bbf2207101f9337ddef65ccaf003a8653c4599b04187a650aa7ecd5849b87d0bf1fbb895312184ddf350964c580d57b4d900d7d07e57bc982d34af6ab888a8104ac89092a5d174205e74de581ca3daa57d0417afebdd6eba04ba152748c29f08f63a1687694e477c2e2bbdbc6786cd07a0a15024fe4866e26e2f137bf22b1a118e74c7c8a55ed84edd5e422cf28175991cff3404651cb65c3ec41531a33081417e0994a4929ebe1f124cbadde350e74b605a2cd93880c06b1e4d134a9312876cb16b589842771b4400a6a7e88cbbe077003aa841943194adef3ec08067a0b255b6f43cf0e51c103d632625080d48f6c6b9d1667abd985c64e9570f42aa9e2451421f3b14e266638d536b1c4ece9d175be1cd5d206c0d3312897a4edab8a14098d10172ab96114a9e42d774b3303c9ed900cdbcd994563b8deb2dbad073aa39218a2811adabb150f5e9eef838fedf5fd3ea3507275b1dc4dd17612ee8881e54083dc51d860cafe92de24ae82020baab39de0bdb445d1e53f8db13a18f0942df910df91e053d6268c190d50e1612bda02c04d3f2829f5563cf966ba364d9f276dc357b6560064e36a1f397ac32616120bfdb11c6d66fcbae255ca6f36778ab28032786585d81d1aae3333566477af3d18e9426f814dc40a8c27049f5a806c0be936da823edd340f3a8a9d382f9460ba4294a6c219bdea53c45a55c0d6702ad1480cd3ad6d468b9af282004025faa64b2230420ed4e9d4dcc34257959976e41b283c46c9c26160dbab5f114a2d598b07dfbaad182ba2658fcb62d5828a69b19db7a85c516f04f5169f517e6b319ef4101d0003929c3a214886a959de49bdfaa16d40220e3671a1e6851017d0966b3017de19aec17f56747faa41f84161fd71f5cfc52d27eb0c3c8da1ac4cbf8c3987b672d1ca953d4206dae948a51431b9882b35bc7178c243bdf93580179382e82287592439dfa5fe68e4ff7f4fad2b98d54be58700142f6f70f43b38b87adddede57de32a5efabeb9dea11330949ac9d1c8aed9aef3e32004bdbac82ab6243a7c38b20eb2cad81309092d1ea70aa922a5f22e484d40db3d1efe6cfb45d8b022928f9f2cb523b3bb535966b0286c2bf5590071daf38508504d6695273d2d650dc550889a4ca02e7cc37b3b7963ae2da64b753a502bf90c37d718852110faaad604dbcac9b858eda390da4e731488d0c8fc8c9ef28393e0754c9d31633e5f322f049d49803537e1664829e515234ef82fe5098c053c39c5df805de2d5d608953c84405fdb3f5f4b57b1b21006b2689669019e7224c6c70b3b22cef164919e23d7487f78a950c0f7e9feca8548a5eeca2b31d9cacb3a3078f3a35676f8d2e59dd2c50514753ef79642669ae70cb08118a3cd8d67daa3d72d81271028882b17ad497246de96ede0c01327f3d4c1fabafcd04d2da71934f71099655dbb646bb413ead0ccf0a8f6729d1c96d345a9b82cc53e1b4882fffe9accb5c283de680242434502d8da134b07e45f58cfa06fbae948a62f5b84a944184bf68ead69300d0c9d23a745dd60a2e4e28b54afd2971aebcb4583543b227e5721fcacfa8f80e19fc4729cc2861196446c225a3bf948f7a847d3220b5b60157ef78f5df81cc1a52f9d6a37c25dfe8b17dcb34ea0187fae5a32394fbe1dd9b0aed76d473512dcfa54f04af8f847f794bc2af928ff3649d8c4807be8518b11d4654536fa20a8e626fe35f1ea5d8a37c6c8f656d4dca7e9ad2345e33dcbb3fa17606eedef41439750f6f7179964934e647230584212a1eefad61306fe998b85f43e7c6e58210e6a6ac2d456d69ae40aa3e9248cfcaaa13b6d17831f382752d12250f1db5d14724268c1e1e2dc438b54613b70a2cf840216c9fdc37c440fad9da3acbd8a6a727e0408c228f6911dd47736e9dc55d8f1a53a7257f5e849e5a8b3c94b875d4165db39641e3932baab89a84fe46836b3ea708d76d429b36323e2321b6f5b49c69e82dcb9d81af6d22d37c4b7089fe4b2660f7dd0ee9d6ff3a7231166ae240b36f3431aa62f662bdf4c857cb22056b056481c3d8a823eff484cc414c7edcc31c4b1132f218e0303c9936ffb07fa738160e7858ce9cab52daabe3d764e91365ed9834bdfceddfedc7f5bb35aef4b6db7b8cce2d5ae5928721328b3e668d036db238d6cff2ecdbd7a5b3f3ac87531c0a836600ed8af6f15cba7dcc49b7a446b75077ad265a29f0f8517da87d507ee21ff52f6b79c71ef28fc4b59371946a8f17152f9708c34918dc0ea5665941611b40cc4c2240aeb36473165a1238c2006693653a534ee3c418c9ad07959479313865e9619a8bd1344972bc75dc39522d1c1c047f62c12900e96541d1a635428f899ecc310c985b83c1b90ede73b2a9b8ba560c431d5a3f81c9a6ba88607509ec9a95eed02c11aaee3610952162d109bf8127a424f65d2b9f02c4505a00ca958f242713ab22726623d644b9fd418983190a3a5dedb4961ece0288e9760838f7a7fe770fa7b5f429d582bfe18ce9e871e5ec29d50f61fd4493715a4b2dd0fa011d3c1b4119860bbfa360543f34106052f300649729dcd1833303529b8ef943d4133ba7b1c0f3f818551481a3032569b2ae83f67060cf81da32e6891a39c2b2aa5b08133103c8c860a12da4ccfd19d1e55fcf043db938f66d67beb2d0f8d0d640b8573b10d1d6eacbf59ecd15ba5dc6882916fd07fdef19189abeb4d84de349fe42996b22da196428070d05f088f18058104d544c212d3474e6d05a1b4122623aeac250d405229d2e80f9f8a734082b2aa5e84af9ceaca190403e948b8236df6af9c232ade11a4c58a4a238402676b089d4a41d888aa9c49c81734d400d5a81e0e70bab9e592f133da6c4bcf512099d0315a4b4807efca8d56f6b3fac6f071d4c8ce5b2a2408a08cff801576272dc85686a818f84458f274e16781bf45a66cce17fe9da99dcb1ca6e97c134b92d87f6b3b890befda57eaf22fe87b009f44e9931110e363f46fbce8dc44cd601e462714c8f824975a352e23c7f272fb6016346a2755e3983099860216fe6811709e511f04f219c08dacae15a27578050f6c2a24f805f83a25e3806498c17360945aa720cf50c43cedbe11199e992ecb5238bb0bfc4ba8f4732fc011548d069d86673b4d61bcb4a77b7f76fb9678b3a562a0ef104dfe109093efd4f9e66fc6f589c0c15b4628993b80a5ec067f44041444cc7a8c7fcb9f4da154c130531149d87d46758a17690ff257ad12f31bb07d6212cd5944d72a6b4aae0ee65b6135102efc36c776929115d47e7158ef5cdd5591955f7e1e4d10b744efc90ca92a0ff98033638c8f0a12fbce1715d3ada870da1f0f6689d06a9b51bbec039da258a4c035a867c114d42d0cb53f307b6ffc24cbefea80bcbd50bb1486a15657f8de070378b11277cca42204789829109c3de04dbd6c0c2a3e87b6b35cd04ab42cb174e5c414a44694d91b990d6b086ae2a386d7ad88b66572d25329762b636ddac3511bfb34d53b861e58505850a3464a870becfc845e88a3e963f940fe3687c0905d06716c2d999ab59f9429f53a3a13a4a67bb4e6b2235ef3736fe711a884a17599356c36d2a1e6f528d5a87b98729721cfe523d8c4f82583496c60a1a4fc28887999c367817acbb8442548ab40da75ca1e1c8627598ff57a774a083a78f7517e98757dab6925135d558b8206088ae51a28b79e1e18aa93759cb4023608f937b3f4b7a7d6d9713154e8ee7a6655c0b1a4e27746086c284e89a1705488dae4c0aa8aadf03f46389629123637c5639035db388116f0306e1d0aa759fe6ea063dddb499b4f7570ed079bbda4dbaccc6c04307737166dd22e9e93bd936f32caa8cab76840108d155d7fab8b8c7887ee3e86330df1bb0089c6f3fc6ceeca21632788421e01c862742a7a22fa64d16d797492722a65a4acc3801f4588dce67386f031e98e5a94ab48ab3e8800f76ccb7c5dcf0e9cabe11928656a32a012081f769735ddff50437f4d14ff7a1c5a80d9107ec39540b2f2b65295381ef1163ca89cac52b7cf77aac593d2a0d4b30c45456a1d95789d06818e4bf5f019fc027e3247ac05554e9ac108e24f8e3fa93ab41226b1aa144eb5659a96e533bd9a3c65c03edd863fbadf2959f4871da02f8f3ab8406dc5f258664f66c1895d5ae6880a12fcc50b9b837f9d24bc089ad409a4a5ab611f9eb35a2890bd2ab1e719b3eca2528162ef6e1e4a57ab8a930a36ce0198b9a80b1883a3cb90d2a59557d2165c89a1b0ca97a8975b9c5ed0384f75a4812b78cc2a60c78eb3a3753846a2cccbc84ddba76afd34780d61d1f65fa343a00ad80041dd82cca21c350e6168ef85c32595cef7475f7cc1319262da45610662d2df818ebe724e3aa7c651402e98eef585de8310445d177a0367110c08ca6e8e55a7f7a7d02769f5a699cff549acf5b4f3965030616859ce7ac64cbc00d9241ccfab6bdc0bd2541fca885a9798f60c98d97223becfd2305a6c7b308397c1f7a6f3365a0936fe955b94350139755c9036e844633ba00976f8fc95504712ab003c3829767773086bd0dad2eec39a890b0ce3aefbcbd882b93f9c0743f89dad4a227181d1124cd81cb1380e0a35c63563c014dd7d20d71d246cacab93e61bf949d1e0bf28e18d6478ce0055ec2bf83747a6c4236a1ff87b18c84694217fc342998841f577f2fc9d02a022d589d3c98c04d107379fb3bafb11e8b8304630995b01c9729d36e88971ac0e792abf719a3493f9d1fc0074942e8fe314e68f9b236de0b38c58aac71cec829a3fe8db68d2e62acb758ab2c9ae546fa940d8df87e16f7671fbae629fe8b6f2b13a82a943d46233cb213a18532cd14925e58a4141dd80ad6ae3f14039b43cfb175d44a628c03ca5c3e203930cb8a493ce9b7a83fdbaf298e3c6a1f75665299dc99070ce56758405971afcd7068ba995782e1db0a419f335e3336be1f4a80003cea1f473ea6c18889b6bd37771a7e89d31736fa4aa482fbcdcaa9cf226ad5f4e777a0436cdf0b60556b10ca3036c822851c20ec07ae90620b134d6a5e7632c64b318415fa091b76e4010be83e9db90be9fe4659e5277061cf14d099e3591301fc898a464c25c0fb98d5cdf3b7de3e240c7fbda23ad68b52ac6ec34fa4fc317177d85ed2c824503467f453990a5a5ce2067dc93d8cd4c6a7a48f290d9f0d953d8114b1d5836799825e5ddcd7af1ed211156443d1878646e8be9e3da3f92fbdc41d90cba91c301ca8efc723ec57a8c3f9b445884eb430c09ce9c92d85b50d71cac11678e6def4482ca6d711dab236986a7c9e9d57c686989dec07128bb64e0aa911be6dc97e2b14eb20989f169a73b51f0736e9299d673a28038b83387cab59c3dbe85fa8773a5cef4405b63ea793f751153fd2677231778adccc0804e8a60a9f2833303fd9312c76ab9aee6a60466ef8a2ba8fec0c831793ccdbaf08bcec73e2d1d3a5f079b326680a391bcbba856b0eaeda6e3f1ec0c7901fdbdecb272eac16fc3428083c15d1671f01738701c87bca62dcdb33d70db34aee127257e0e8764591c13feca9807810c9aa0b6d84e8871ec41ca406b2f60ada1df75f63d93422fc60c6c864569a07534e7706d50a5ac455fbded9d8aede1f1fb331b174828111d8d711179d78ec181f7ea742f63f0372a77e4d0a32b6def07b388aa71ef4d52772a9f9abbef054c8f3ae287a7e9f43e8e8717aceba49e9dfd477df09628e33911cb67fe346402f4122a6c6a2b51cb072e407b439d3b1db047e74e3588a40f5458a2152e2e3b714024f72c5dc871045b235919e8e2c65b7d6b3dea08173929c5c31e665d6c21165a2bf5621c23bb9a93600d963375c9f4259ce85059e46cab17c1fff00fab277f652adf3d6619af6187a05872883594e24ccdc67938be99f950d322c15f56f3e7ab3e40086e6eabaf4f2bde8b12970fc7c387c2dec600eb30ee1ef1139ad0d48b026f32e223f1d0583ad95e53c4757658842fabd9324a02ca124f21242cbb5804a6b84f612d9f9e501ce6d1616b14946d6244780ded60f2a171693fb270a081241dd42a487c1f60e71aa5b3891bbe7f7b08526f2fc385221d2272f033a04253aa59d6c7792c40e34219343a874d35ed3f8f495217ba7e77d16c0c9e6be46c26371904babd17561da7dc74b955e621e0295ec29a6a277c0164f8307c9365a6938aebfcc37d5e909eede46b5a8d2bf213bed003a2a4b69d2997c41137137c158ae45c0a444ef98476838d9074d98f8bbf7d7012350c2ac50dcdaf4798bbac70d3ad51efb04e07917a72dfc4c37409e4d184ab0f8ef7d0f36b0120022869568aed5f83228479bc1d8da56e941550923c5ad9209506ed7aed4570935502058170fe49553296d371a7d56d7182f8ba0f3f303043f32ddab7e4462ad767c2da6d9fd62d9ef2e915c14a06209a34865210455f4beed984f50776ece22ad4016d60190631381172dbfd4872dd27851c4d040b0d074d1fb11319c8bf43a30e2c711d207c45aec077400712c13df037d7acb08b4992e360fa4a99b9cf28afc492359b07c84ac06a86f3da4217f1135dd2fccb7a2011b984650d9f450a284e41f53653c4bdd301136d344174627430599e1469e13ef831fdee3cb509df033a846439459044ebac43010551030ad8a736df0b765b1b2021a1cbdac0488104b6df24c05a80c1a4510957174d455b475d07831b6b861653be4d28d92c4a4351cfaae65db7febb32c5679bdf6ed7ffbd7943dfb67f47c44cf6c7737c8a7f1bd490aae121b0fc20e5c6c8942892278461118b0ee1bb41a460478b0910133b48082000308400a54005044c444e8030f32c0a00a2a08a04412037021d7c1908c7ef0a3031c58c167a50f3e4a40821450c800031b34b8d8c2025ba2a000133720a000b9009c30a58868488c30b6c080153c3459430d31b8d062cb151120a206196210011e686460cb15553471c5ca974341362c3ce078a3035e6841c5144f1891c301becc05200514a60c0521b9e5d8b0c408c38b1f7ac8e1ca9701286148280889c3f18618615c600b2aa6788209231280802b2a9430a404c98d1673e5546083f23ee078830317f0620b2da898820923129003020e70e503391552208167dc438f274e80fcd8c0101c6ca80100535a0f38400dbfa609ccf471e2234c418c1e210a5448a08e088d088b005fa1ebd402c79a9dd308278bc107e1109003990d4e1a804250198018cc5850410527110564f543646db0d2a066088dcd8c8c2a2513a652313127d053576842f8c28acc8ccc0cea876a263373ea6095f402d18d0a2745a3aaa1564040d42a9543319d58271b940662708095d48f506606a5c3eaa09684151a29ab12a5f3e38530c6414c1414984a828515254f806c62085134a8273925b00202188e7879018cb9d97cb0e48510b5c169270589f4c48c90012ba9d40746572e00e223c389c90b3458d16066ca2ac82a080b4217a42a4d43022b49f60c135d02449999132b460955001b5e50a17ea43a0081c05e88e3013347ceb8a232c10e3fc29a98253632369493476109182c88c1a111a211219502ea888c0d860a08c10486236ec0406c5d61914f0cd0c54acd6af61003eb05049432e484444683d90b3432b6148deac78936c4e4d4018d1009acd0c80cb190544d380812a29969b2422a9354c04a06289d194dea66012b4553137413800858a15135f16057839598931803a64c48d1c420491de9f1a959678ca89aa46864346001cd144111cd64106488081b60704435829323115ea0518d3230168c366424b342c96c10733bcd665628237689cc8c0c0d8a27c609aab6048ce1e044240683d68c8a3cd982900851d16030437be187a826a6c241154115491d61e5c80f1450cc08311ecc14410d01e23a7500839591073c99a71dd42946003701d0e18a8c2a9184172e4b287104110620800b4d5ac97c52178014a800404a1424a3272534b9d5603822840082456da1451f77d41107093d21dc6ca0814c101800a38808981044c2ad830b5635a92e554c31012d451031841041fc1005c708cfd883039290fad803cc8eca1d5fb6e4b24d026ec0660f121a192d39e0409b4943034668b9d9661904a9a151c90ce1200a0aa4f065115109434241b7982ba7029b1f2c14100828000d15b421b34289248e20c01429b4999020a831bce421a547c96c4626c5c40c5334f10006e8508300b20001c40d45443d488cac403d4e28084d80c2c37e09e540c5117a20a60d508d108df08c990caccc08c708c508ab08993829112aa1aa8045c00f541dc40c61691066105e0006b1b120a682d00614513fc29a189a7046a50a65646242147802c194bbfb08ee4e33f3105c543d7177ad8e7fee2ecf9799c4e8aae30ecea8accbe0a1b14db92863017757b92853017717c145191d44192caeb366b63916c7da2fb146b1d5d86e6c91fcd4abde1e69afdd19b51adbfddbc9f064a6883248eefec34599209dcef2508c7e8fc9e65825492c8e922416c5bcdfa9308e230a3d5eca86e3314262ca8b6b7544d5dffba63cead6a926dc1d0317533e7889febe56a75a28d1175325a46ab6b779e89fd6ccf6497c6aab6673ab776bfb3cf4aa33a3cfc84e2b700e9bcbf658555dc923f666b3d56c34dbcc26b3c56c30dbcbe6b2b56cb65aad46abcd6ab25aac06abbd6aae5aab66a3d56834da8c26a3c56830da8be6a2b568b6596d469bcd66b2596c069bbd66ae596b6693d56434d94c2693c56430d94be692b564b6582d468bcd62b2582c068bbd62ae582b6683d56034d80c2683c56030d80be682b560b657ed457bcd5eb257ec057bbd5eae57eb6573d55c34d7cc2573c55c30d7cbe572b55cb656ad456bcd5ab256ac056bbd5aae56abe58edd9d03175127d0eae85bd1a6960a343a3438382e58176874680460ad8e1580b5d65a7707c1dd6fdc9d88453d616f589adced4d4449419120a278dcde68ec6d9b7f5a4ba3db3d1e56addde69f3a7df5f7c87d7d4a8828518272f1d486bbd578af3b6b35fedeb6375da26bde5933dbbd66ebee1f78e04e73b3fbdaf295488cdef444040a37316ce3d4e4e7eeeed5279e54707b2b7f677fe57df5bc5098b2aa661ff34b72776d35da4c1683bd5c2df164c4ddc751ab63997d3cdb67be9ac99c473391cea219efb448bbfb1417c39f56c71d3eb79a77d7dd2f70b7c0dd87dc5136af135c046f70f70adcfdd7f32e599daa06e92c7a2d9aad2e0727bd2a13bade6efb5cf51fe96cf9e779497717dd1d88b36adc9dc6dd67dc5da5d5b135c646d70e8fe96ab564aebbdbbd5e25cd65ce76321ef5efab92e34fdd1af35ee5c93cf59223cffd61dfe5795feef1ec35ffee13115ce2ee19b8084671f7202e8238f44fab2489fd9d2dcf35f7b4cdb11ae3d8a62f51acbe46718ebb5bf7b07477212e8242b43a6e9f29e3ee60ea2d7a6df9a8baee5737fabe3f531fb61b5f755b1e8af7be3e359bf606e1e003dc7d0317bde7ee2c177d0777bfc04587a1fc135fd3eadfbf9db566b668c6fbda9f9a7defd3ea181271f7212eba75771b173d89bb33e5d3547f29dbdfc6e44d2fa9d591fca51cd355794855f3f0fcd4abeef0e9ee31ee8e5aa9595583505ad450e35cac58c1f909c50517e00c33a02c68432257b0807606d4cd0f04a87e3020498d1751b94fcd8ac86a15ae425fa56e381092b12925999b227ea14ddd14f13b591270288b5aad5096260c5744666cc8329ab12822575ca8b1e0cc8ac80c24f89c2c0d912b58409f939d81041f94055944409f308c51f9b0885c61592cd687c6aa885ca9b133581f1a0bd6dc5839b9308093a5b9b172b23496e6e60a16eb03da9045e56457445c18c0c9b26eacd8930d698860017d68ec0c112ca0cf8cad21726506127c682c16d0a7c6a256ab1b176a2c38b3ba71c1ba406343151122334c6005127e34405cc1fbc0a56e948af885b6881fca8a4621cb08659d6564533cd4fcb8e1070eaa13cd7d402258422c44ae6041dd4c6005126698c00a24e0420baee05ca6d4a84eb4165a1840684f7600a045d1d816ae60b13e3149291b6307306355d65b98c1fac4dcb43080991b2cd6e7660029abba91b919400ab4313727eb76002910adf9718093ad3911518a01594c626e9c15e4a4c65738f9a9ba02c30ba10d5128d4098a1a074110ac514a813328140a655356660625631d75b22a95054f554e31206851a7530b55403b6341540b4ba155ed9355d9989b2a2de05427550b32aa9b3075524acddc28a5545665ab10005725b42011a5d44d0bb82aa18d59a2c6ed504c15953df98cf5a193ba513675b314da9445115941084e29651d0712a982c2d1dcacd0026ec686445610824bddec531310a73ae1666eaaa054372bb48093b901415cca9e407b2282ba59c1715e440015186251c329d6aa81a6864275b18243a14e27192e20012e8507972ba998308b2c9a8849c55a496039fdb04125a3eab2802e59705584e0aaacc2a44210ad8e0d574221b8d3952042441507445c91220a30bc88001458d070ba423303034284808b971eb6f89c4e38ef82c595d3151c78faf243865a0a6849130b80b1000c38141622c0820f1c0ac5010b68f498030f2b3b5cb9c10c94152498e3034594e0871dbcf0416151060e85051aa718b81062a9584971da29c2468a1d20b0c03283450e2cd90f2e26e032879035d09d130c5a14b1858c386e98a1cbe9851ad4d863043230910232521086412c3e36700015067090e182458a4b1533c49cb82c61038a0b11369cb8f06003171bb6ec81c30870555a35706c89a386d5960d18a02634a30034626ce16245c7162b705584d480408c91628b161c5242ec961f6a88d962430e28160e5b661092822b4460c31575c8d0e58a2bab2bae8083862e57a461430cea14d65641865cc1c51220992c61c40811050874708193919191b9a20a3126704514b82aa216572cc16ae20a23502a1a9c77b922cb1539e0aae0b891412c45f36409509335b0a8a20213308095367258a38e0828408d3db01023cb1338541629809caca868ace0e3012ad44a0808822008822088b24bc2256ec22041429c9f7041c2d9298cf1f0848af971b24b42944ca88ab12ec3627242cdc4a8584cc2540dcdc9aa647cc6d6a8565284386ec51cb8134b08741cd0828ee3968675b2353527ab441beec508c96940a7d34d159d93afe1c50800494e37b52e49e14c4888030e41e1625dac200377c279172bc4c051b145901aac5839f1441248ac0a60c503ae58d100161656e0b03ac30a1a5836f04143983075d484c00d37d458cd31461c5dacbc6ca9f90005580998000f44b0aaa8a287d51a5524a08c2a0ab0e240155656256081f1c50070b0b460837545192b3c2eb082c302ab34a4d880125f44a0860d2a7e603d4185024440450d351ca00286951b2740001e5dc4b107075818c84013600891c5ea8b2956126062015a56464c918515812918c082c0140728628a2b3524d063c5c5086ac808c1ea4b1b2c2ad060f510c64a0e2d88a8e20827803882a5800a08c1a5023cac2250811b70a8c00c144801eb0830357dd0b142c1075648a451a3871756105c60b9a2a60350ac1c20810b4831840f523860d5871439b0789002cb0e7db02e40821a3bec78c01b2b0cac710319ab1178a107979a33a2e023091603204046143eb06a884287d51351184087285e588131c10a8c3b6a4e0082950936c09ac20cd6125ffc80458d072ac065090f14c17a81024078a1c002b450c08695181488210c14ec208231e4607dd1811509cec80203ab28bad47441056b8b26585218c132028a20a0d8210350206065031432acd0d88335441e56cc5153020f74a1811a1188f1812d565e58c102e2890720c1c6130fa879e38906d4a8f1040e2c289ea06195003e5856c22451c74a0a3750a0460d1f63b026d0058b892d3517a0c0ca0213a80143c41d4ef490851309a869c38902ac7c70c2caaa8d12acb61067beb01020cec051638638c3464d1de24c19ac31bea841238b2c54ac7e7062950712ab120cc1bad2440fac1c9ad0a1e68e266ae8d2440c2b33f4a819411e0da083e500376ae058635545192b19be582d91c56a0b15ac229c606d41428c216a30c0440f7830a1430e4cd450f38589185675e891823c82a063d50137584aac51934619ab037cc18a218bd50b54bce1441724b818a2c6044bf4b0c262091d6ac258a2869a304bc4b04a811e0dc8a30274d0e0064b88352a50c64a035fb0b2c8830a16124e5000091886a8d142891eba28a1034b06256a58294089189cd06315431eab29e890c30d9605d6a8c94019567c5146165e50c12a80130b40420343d4c491440f357424a1038b8b246af092440c37e8d1431e27a0a3660e37581258630545192b35be6019208b3da888c2899a0d20b12262882d7af042073b6ad022aa70de650231d480b1076b86300cf8a2441cab19dab092064b07327055dc4b08bec055b1109040171baaba48a00a2c28dbc64e125c682d98d38aac70608842d26ad55044c25378d342e200b8b0013e611886e14d18b20e8004969a3590b85233863883f32e47e881038128021c0db50ad2e58833703438ef720418b82a42f2a8f122a0ca5561cc0955e564c1308c51028380372b1ae82323010ec89060a60f2d82a431f38120b532c4536a0c21658841440a5c51825a19373de0648218c202995a19aa18d4295cd244c4791723bc9889c2080b6031a20a198c80820621422c8e11448829d509754aadb2fc805b31610403702895280ab14270de450b1f642c097340eb1c8843c42042c45a8803da24210e18868803e9103b20cec4a04ea1280ab15a3080ab82132304ab0343421c2d5d0a40c3840e334b9a6881800d2a2156c89015162d59705a1090432a0539c484c18252e121c3494646952a628e19dca0a14b111db8224488c5b15b70154c0057a5b5e243a6065745148764b101881e581c10ab3897254d64586d88559ccb922645f450040eb82a387bd40451dda856082d13c7e10c51b92d524466489122438608014521699c94626e82ccdc50e3473cc1c5bb18c1062eec7204165c78b3c3504585355e64831fb1ca0a17eae880210940286a409ca3ba085107ce715ea48a684fac3ec4d38d2ac4610161f9ed07ab08652544a2460a8aa5812245d040a18ab0cea883c500387ab80108349638230c3386972e2618e28a31a65802850a5128d607849c4e31283642168b454691202c32dcc8023260a6020d1803cb07c0386287379ac4c0031a57523006971ed408c16809b59c1147161cb22801c40b593800a206100e570301594e902504202c90c5c578230b0840d4d145961fb2c48030401611081e204cb0811c208488e108c703f4418b30594490e502205a568005a0712fa01860185a64b9b24716191052b8075290a5872c4e70e167d498c89280087c00048805105fb2b071cad9124416274e80431c5caa0481c507acf88109106829a3268f223400c241904aa5522a542a954aa5902841162270014c1aab5bbf6a7f1b931084fba8136aba5074b67cfd2cfcab2691484293fb90d064e7d5f4a75e4c62f3d1f72141888fbf7ece5b4a8f97d2e61e1cb4fe258b3b73def5e4ca33ffab668ee9d1adbfbcdd9294cca1c4014a82941051824a82aa6b8f3cb5cee9f776a9668be29dd1d3fe141cab5baef3bc6f5a34e35fc29769cd3d269ddd19a1d89b4e6777c66b933c91048b8f49454547f6b7bb3fe393bb8b467177232e220101121c9028b9c571dbd272f176c38d00ee6e828bb7d186868d00b69c9a1cee3ee6fcaa6e1c9bfe2b599bb7fddfe855efcab33af74fbbef4ffda5cf797d5e13e7925647df9e69d1d756671fc53ef5377affcc796771f6b633a28f534cbe7ad36dfda5ceee8ce348eeb73a5a47abff43ef4d5f4d1ff5d935ff74b092bdeaaad1b739c9aabfb33a39ba5fd5adfb257cde5cfaafa4c3b15acd5647a77c25329ff6875e8d5e9dfb737773366fabef795e52c51b156b44f8b8c4c5da939a8fbab7d1ecb3301f6eca6a9a5e8be23fa7fc363ef10eeffb1a33edbcefa2bf13cdfae213476383768554d7acedce59dde9934d6f75ec266f53102d355363c680d990ccc559101fd5bcc9fbdb7d69cf8b37ca94cfdf598dad363234aabf849f7c5527a4d3ba8c5e9bf7bf6ada12af2be0334916846ccb6cb137dc753afb3a8a2502149484fb093a320a02b2fac6846888e8083765d4ed9c559dd5581bd1f67b5be3accfbbb3ea3d57deab495c8c31c59408713116024c0c1f41387dbb37ee3fcf6b346e7255b7b6b771a397c9aef967df87770836db789e5849055d6fdf2f7339f54d1d0aefbb4847e590fad81c5222f393121f6934ebfbea46d7ecbbe8b3905553f3ee2974d5dd14a479f734ff555384e9b8fbf2d62b69af65caaaefd5c54bf5f1b7b3e5a3bfa4f3c62a93d5b1d7ea6bf59f3c146ba5bbcfd47737fabe0ce57d78bf54bf53af192fbdcae3a2cb8b8f5bdf13cd59dbac7746d1fbd38473d9abded4ea6b4b7b73b1c586fbd8db2d225aba31e797f0e95bf5ab2d11dc1de7220f1c3ee67c6ad56ccbc7f9257cda6d04d5d8a63bf5fdde38f551a6a2df3bb9dd927a7aac4e123367d528284953f6c9dd6e493c335ddc3dc8c59100eefee3e298442d1fc5e69756638b7be4aaadbe56274717e57516c5b6c4b11bebdf89668cae96dcd1c2e5e28e027670ee1ec5459d3adc3dc9451d1e7470ee7ee4e20877ece0ee00707104128ee0e1e3ce59b5bf9dfd73774fb468647a54cdfb9ad96efdfb9efa7b7bd496e9515dae7c16725bff2ecddba2392f59a647cbd7776775ce3d72a3d8a2af74b7fddd3df57ac9df59942997dafecefeaecff648dc63c2b1bfaf4aae655653dd6ab55c76e48d6fcbe7a979e7d58c7e6fa33cacedfb56adaffd9d6fde1e6fcde8db1e0ff7be976447f5b779adb63a9d2d7f93795b1d3463ad5fdbddf4280fc5688f2c9fb4eaf37858dde3d6cf2b2fcffeb66971464044b82326fbb5fded9b898e58718400eeaec4c5233e475cb5bf7b52a7b7f3ce9a437634cd6ccbd728bebc6bedef95cc9cd5d4aabf64cf1333d932f7f6fbeca866bc747bbfd3d8c763d5df79c96e35a77ff4bbe7973d1bfbf093245be3273f5cf925ba669fddf428d1beb9f2cfb5fc1ea97b88ca27d56c2a91f9f4c9e508e1e0e1ae42ee31591d1e8a7bfb7d59a36047adfe7edf556faa66535f55bd16bde86ff4978270b82ce19d6295d45fdadfe130b9fbe82b7f5f4c66adef4ae6d29eab999edfb3bf747bfabc5a6f5c7bb9d62771ee91af74b7de79e92c87804afcb3d1df6846bf37a54471b937a5fcf21fcd95434625ce6df2ab6435dd3d7d9bdc6e493d7d7b3a9be85036519c33d224bdaf1671d18890df79aea60878a09fa6b767d1222f65bade744bb8bb5b14270216f77d5f25adbe4c381162be8bdaef2589208a2170194bf4fe33eddfd9d7f755f24ba67c9e38e66208565cfd7d6293041743a8b9fd8d75d6b757fe9798c42b5291369ca7aea9e5a1f8776a6aed93e8ab6fbe6ab3fa2ceca019b5d76a9ce4ee3e2e1699412ce264945266f24bab66ab53bfb771d0485e7537bdcddbf6fc7a5e29bf77f282ad5f66b2e7f74e7eaf149badee9ee322085eecc6beefc0451088705f56cd22174178ddbc01e4e24d10ee3efeeffc7dd59524afcd4bd9f72a93fdded6bf57daad5aafd6e6bd733ecaf677be6a7f8a3d72e41694544474e4c8d884cd57d37d7bfb9ee7b5e7899d883754845cbc09e2ee3ffa875ecb43712eed5e339139bcb8eb725c4aeaf5bd6a517c7be40e9a5fe94221a2c5f71bdbf7656df723b97b8f8b445218d3fb3dab8d6cf47d6856f112faf677ee7caf356ef753506cf5f36e6acd6cad1d1ab868cd70f7dc7e9d432f6975483add9799d45933eb7e74f6d5ef31595d0e921dc1fee014e9eccef86526ed7ea49ea434efa08dfed2bfbebd54abef5b359a49bdd1c72436cd553f896674a36bfe31ff48ab5fe2a5aca6775f2035e8764bcae572d53db75b52ae3cb355b90a29a828a460c727ef995aa59c37cedd895cfc600977dcf4f6d16c4d6ccbc7a7c6e59991fc36dee897d896df233766d276ebb58a552105f573b96a1452b0df4bfa40067777fbbd8dc4f4684f5212b3285d95727e927b4c3de7797f7a78d844f38fd5d7262549d2947dcef3921ef4e181161f7fafc4f4e8b5393aa6477538b61cc2edd7a20748dcdd898b1de031fe4c4c7eaa6dde567dbdda5cae484ce5fdad51ac739e9734b3fd5c6adc4b82e26207a9bb8f3be723b1039e0e6ec421770cb9634807dc1dc9c52136b88f392e9c22bbf3dbb8e9cfd4977f2c08208cb99cc6bd6dd5fcbda4f15af49730cf5e5f564d8b621e56b3d5958ff23092ee3c2f69edb5fa7faf6473b96afdfb9278a3dfdb2baea76f2fb7d1576f0ab4f5f370b9d774af45b75b4fdf2644eaef7c2bda44fdf27eaf2709f952744f6d4d42be14155a2f155e308ba4b6a2d0f4255a0e6d74cd2489e3408654cddda5b8b8c113eefeeabaef9b56a7d135b7a4a4b3dfd3ab2dffdc5df4edefecd6abcf96e8b527563f6d7aabb1b53bbf57dad15c636c6cedf0f26973f79abbd3b2890e7d79555e1a65ebfb046f8c0e0905fdce34c5bdbe3d231e8ad58d74944bf5fb2ebac99bfaf2cfed467e99c9176cedc9a99f365d9f1299cf9d7ade9d77aee9fa7a92345ddfebbbeb79c1d6244dd7977d49a89039d437d2b7db1325329f3e3de5a72956b7bef9f4654a02166500c6cb5fc122b1bfb379dbadf7b95ef34bfbe70e6bf491b43aea8d7df8f4fdd63f18e8ea1bbd2f233d9a8992b2890efdd4bc6f895e28e7553f2d3aaf4fcd44a5fe5da34ca4ff04776fe2eea88b186000032daed5f1bc22063eeee3381ad97feaecfb2acfee4c64476dcf7bcf953c2f697b1b33591e8a4b74b5af5efb536ee72ffd99dbb79ebebd0becf0ade69eb639ef9ebe3d7b7badfed2ab161dd2ead8d3b7c7c3c3d39a91b19769f2b85eb7458391b112068bd55a4fdbf97dd5ad77abe6d17bcd4ae4f7489e8d793d7d7be87abbdd6e41d6089246102d74f6a6b3fbdadf776b5bfe4e5755cd3f6a56d52077372242f94beaeb3ffa5785488d7db79b465f29efdb4ddf6e5bcd69baeadcfbae6a3e89def3fad420b9fb1317830871241668f9ad1fcda9bf71ce975533477eaa79c85fe279b95c30db95dd37653558097b9b4cf682bd6c33996cd7fad935cb5a6be6fa578d87dc7a5579440b10f0bb46ff664eff6eaaf53d7f7ae4bea9c6405adf939c528190d64f922b120fc55a3f995523bdeebb918478d8c7b7e2f2a1a85fa29f22f1b08ffa698a93cef3fa90f4a7bf7dbbf1505ca2ab992bdf778978284e539ce41670f700b86803c64fbccec6d74e3651bcf3e72575f9280f6bdddbd7c8f1b0a273199f8ca31ca504749920c032c606947120600c06e00f50f542d1fca400940732a14a15833a81a067108e1e3a68028a04d06590c4389014e82008c6c4c4c29ad041103c854d4050060c41f094622104435026c4e0080453a08c87206b3c85a9140e8d05516188c20144816048038609c1198f0151e1e941b04a0882a12a4ac8410886295408d21182602a044d003300c153a8ca22e624f303c400044130b48517a84ee00f6008a642100c4f393044d1ccc4c215d8a302638a9c9480326188729d68c210cc013203058072d5089d8c42477908011075c241c584e0c96316007e00f4d0455174d0411418825f4055780404c398303c81200ce40155e02a0cc330f5031a09435408b64e638d062a114056051b843b8031a106200a3c811e1a859606447988536e75e0014e4e240bd0976c119ec01488920159e089873054dd4e60ed048621ca86ab191c300604655a28d7072d30a449a9421004411910bc8161b8c62905ce84a8d3127813ae2078da01676a42502503864b420f420fc20e4e7e721008980a439085124fa85083300cc1148832c19922604d4c08c6803f682008822007e1904ccd4c08ee542b991034015481e02904516013b083d0160e096b429930063c8121188237b083b026944181a7981308d2c0138803ce802b100491801e843427195085024310bc81600a833008880ac11814680b6bc053e801783a85212a4481b63004370869c01950850a4110bc811d8434322710a542812048b3a09023aa24a71162c0133803a284803333b71a1438f00004010586612a947950bca055d304044330044170c7653c752a0400f138e18bdb138490c3cb0944dea863cafd4012513cd086271151c3918418e391ae187e2444153f1c11c945260851050fa31b5bf244080ff2687287b46c4208e1b1dd1f48d03cc02a42234203ce3c00a660a6eb038899e5c3479d1e1eece8b4843a6d4420ebd8d6911182d0b9e3039d5d7d6033a8c083b04005d7c5e4e0161583fbc9773f00e0ee025853d0eaf8e925f5bba7e06e24fe10b53ade7dcb1c0bc88983d315565007b0827a923a624199302f8811b01213b39211418d175042b85019ac88402265002ba8273318d00c11128395991350544e1ed4ecd44a25a4aaa5684e4884d0c47c61452585952029bc30433bfd30825507acc4f4612566b5c1195656fbe60a2b33427e8432486278621480e5821a0d7680016583152044801039edd8f0a884d8f40143cc08aa9a4c92139053121511564e505c3c299a9404ac9c6a521ba0746a5c2710b542e9d008c58c2073426a83144daaca2ac82ac88c90190c644e38a50086d406a7256088e9c24a0855503e9b0ba103d46d05246583024f3ba71f94d12a8354283345860319a2980b7e80a99a530c6a753a9dc293cc49759aa119823ac94491a90088118d8d4c4a063cc9a062281e14a8d2e0c4c10b331fc42061e587045eb049a1385001c180ea000c3739eb7df011471a1a18c38b175dac3ca41c7008f22141092da3777ce131220251c9a040a588a88f30cc20e3831b5a3cecb0c4011d40438c7e73a20577d189282e3a01c545278054597d2a3f9f250c1425c68e26e270a226a936f21e4410c1b6ac122236a6276647041194f4d8127d4761817ac2510808c11d5582a386504d504898b06ad63c48e42fdd6eb9f2c997e24bbadd349ab1467f37fdedb604e94eb4d50cc5000e064009294a74e1aeb3fa2cdc3f4bbdf1ce9ab8a749d908db21bfcce4ce6f63f5a6681434e3db6b0a72772eceebee5b9078bfafb7b18a73772dd057ca493f998887e2a32ee5f5c902a57e6f178d25b646f4de3aa76389ed6e7af485b6ce46b89d89d4d75132d1d08fdeb8296868eb6c94f5ab194a26d25be7f4a7a35acb2bd155a844f34ff93df2669cde3f32cd223da46621bd8d0832e202023dbc0c10c81148ec948f6223f4d8ff836620f4f6925ecd507828e6a1f7c82cda6b115326d20285bb6b39c1cdf506206040c2dd975c2ca2052cdc1d6501f7d6eb0563ba47e550d397e8c5e5f6ebad739afb5c0e02bfdfc63b3bb9fdbc1c0fc52bd3554bad6f9a7b9de2bd137e71af91c29d0d77f7f3014d78ea239baecfa7fe5251167a1f4e2a7f67a2df59cad0ce4c7e76262a7f67a29da59468148d432a3f88e9aa404d174a896228e8ed2569fd4f7ae4be5036fa196867293b13a9595d4b68ba501ecd443b13e92124f5978a7626d2392eab669d168eb5b7dfd966a68dc907b2b6d4b85cc21b7d12c538fdad6f1b7d9f6f08c52ad313d99ed80ceb6bad3def2ffd91b56a2691ecef2c251359355bfd2758359bbefcf33b4bb1e5d06f67b545714fce2bdd8d64757092902fc562145fad6675ad6251a6ab0259772fc1fd149e829c607e0a72af122e1126918070f540c8c6913b1108c2155680636cb6eeecefacfe5da6f33ab87347e30c7723dc9d002efe50f33fbb33eafe734fddb37b0d5e00a67c9a2a0d4cf9347f29bbabe043ea5668e9e18b7bceaa2c36bac6d68efa3bbe15fda917c5bcebc356e3cb7bd45e6bae2bb8fb91170cb87b0c2ef600e4d54c6a07186067547f06fa579334ee5f4d7ab408bdbd24265baf509c34c08b37a0e77e9ef7270bf734dce07137c3a1b807b90fe1ee833b20abcf42d6175fdfabbe030b2c3469c17f3bfbd7bdc9dd5b707187237dd5ddf4be4bf4af266513554d7468eb6c149484e6adb3d1dd1708b7b3a3777cabde5180167f8087192800a7c4d7e85f68d479eb14e333f5dd67d2e952dfbadbcfd3c94876dce86bab4bf1d6eae3de33692439235824ea6328ff42f6881dc1fe9b391cabc4a678e7f69f487252bc719458bbff54b390e5e1b1764cf1b6bf9dd5c949f1b619c5b1e5b380f5ceef54ef897de8aaee6cf492baf3fa548d668ce6146f7bafcddbea34d6fd36563356539d5571ce3729def6bceb56f1d28dfdbda6be4cea748fcf74e373f5e16c925094f2bf4431a9ff68a83c42f28172224d51f74dff68ffd6f9c9d62b50f94cb944837ee8859273d27d20241e8a7fb7443f6d0a42878e9e0ce9f3ae255ec2258ac71ffaa4e94219f1d66fcb20228f66bce6a56bda9b81eced517c5a5fde3bfbd831ef35ff569b363dbadafdbcdff5e935ffd021fb4b162903593557b93e7b6dba5af4d37c53d258b52853b6ea9a7f765f9bb5cda5fa4bcf949be8cacf65f559c8bdba92af51acb3e82575b2fa2c1cf99de77df588396489141d0551b144b82649f62828091774c4c406f9d824a42850ec887e9aad25b223baf76bad91d06c7f28ce8efa92bf13cd5aa7d3a9b7c49a87e6f3ea74569b66fef2aaefcba4f5b1a34eb7e613ac4ea7b32e587bf705b23a465627890ff790d20b4589fdf4b7adfd13a7be4c6654a7d3055910ecdd7aafd99a797cbd31bad2d085869ffb68c488bd9538a7a3cd57ad8a8454fe5256d314b73311d2a3992897cba2335d8152dcd368ce4839271dbd5eaf91bbfb5c9c01e7333333341a859f9999199aa0a60b65cceab3b0737ee8b526c6e1a178e9f7cdbbf3559d5e9fd5912846b2235637fa4dd767bf97d474a1bc5e2d6a9b2e143b9e59d5e97600503425495fa65fc29bbcafa43eca94b77e5edef6f1ef32d96b99ecabd97cf57b3ba43e5f7d26fb3eab37b63d121fd9e8d6d9770468e8fa2cce8e6ab666b637fbf54d8e7ebc2d897d19cde4eff4a9dfdb7987839226b6e3f3d0671a7374f6c5b1b737b2e37e6d91acbb3f9a81342e972b52f9f37b27252e7b2ebee033333334e3cccc0c4df93b8747ef681e1e4de67207c5d7c793f1386a32973c3c379deeddc3510105fbbda49999199a5cae4856c070777d5fa36b2e97abcee5aaad14dd8d5eacde2514df8de69fa4f3bc3fe38e14273a77de5cf9fb92afa65fe67672b9ea1d1724e0ee3cecb319b532f8f081dcef8ce6722018f72edcdd9d26268c07dc7960801507e2b3c4c119827023a2bbbb87239c8efc94fd14e460100e627150c9dd7f2e0a00c8777834cf4fcd391e8ad7de56ef12ee699e276faa95d4ccc4a381968ac060014ce961b070858551af2bbcee49f2dba5f77b370f4ff83db12f0f2f5df0d13f89981e8de9c1051a1d1d253d4a6c4e538971b6f94f6e476ad136ff89b5d6d208c0369538f7bcf27329db4c4a1573820b28e931ff8cf1424992112cfae769038e6203e583cc90ad4bab4489cdc141ff1c4975d7b23d1fb8c708595ae2ee5f66d4fe76ea7b7bfb3293b77cf537faf6765e3488d0875501eeb23be6f9da2a51627be3f3ca57c72f337a42d2d00fce08f76393d8a6123b98ae28ad8e3f75a3cf935e1fcfbfba773c1acdf896e253e897b0d68ffa7289dea3abaeb95c912000017727c2dd877880aac5ddbd747750e5c1c13b9a4c391a42861688dc1dc4520a400e25422081c1dd410c1c81652c620701387077300a2a84b8e286216438b8fbe984f408890c04e9230f774769798011461650d105cddd4112128042110e05f8a10a773f85b123004d48408b06d8e1ee600d747c90a3c62441c2dd4133762f2d5238c00a1b70f7f049100970d460a48612b8fbe9821bbc7cd981872ac870f79403166002d08c1b1f4bdc3d7c234c0e2decb1831b347747f5a0936940ef8b2a1d70f79d2f330a65d4e9ac2d55d069241d0ae3639defd5fc3322d99f7a7b3a7ceddb31681cf79aa2ba7fa19ca4fb3747dd79de1fdd9b3af6a2d8049be28c92d03f77d38a531c4e71fbb569e631e72a5d9c5f57b03969d4e9cc6c718adbd8e7e3ede79518a7b8f1779a662ed1db4371a9e64c9e99cc3baba9d5ab465fa3f975462f89f7f5e5bd5e8bea68226be67be3a1f7fc32fb2c0fc57a1dfff50ede99f72856af0faba9d5d86a5c62bd5379bf513c6484b3251e4b8cef92fae5a9c3a4f676f97a55d7bd0ad911dbfd7a5fbb516c75768f87add6e9beb7f476d4af5e3baea5fd6da5bb35dee725b19ab7c6bd335d336935b6bee75dbdda719b19e77ee36e3a57f3f56ac79c52bdbdf1478ca37169b589ad8ed6394a6a66da8f92e5cd6a4671ce4be673a74f92b744af4e77f72ee35c8b7dbb15c98eae1d8dad79beded8fad89c711cadce6b041fabe31ac10767a3b7443f6f55c7c7ee3bc58ee56b7d53cb4371f93cf4aad97c1e7a4bf4daf1d75f5750df547fa3d8aad9cc8d7fa2bfd3678af7b9fa1e6734a75aa7b3fad14dbeaa66d266df5ef3eea2eafdd3e66dcf9cd3d7d836edf1aaebb529eee9acf5938fda21239c1d47fde8f3ecbe56e3dff963c7abae38bbf3395a359b16a7d7ead42f4fbcfae4f48f1ef34fbd4b634f63140bd97167d4aa5963fd3d126772af19bd16a7b82945234eaf1d95f4dda68964f5ef9d8c38bd487604cb74557c96997c20de48e273b7a6b67c1bf404f78384febea43eb2a3b6ba9ab9579dbdf6778f2f5eb2e5eb9c36bdfdbd526f63a6b1fcd7bff3bcd9de479f892d1fe97d9789ef32f1ad48487fae65869294cb154933b13a4849d42c749ef7e7e776a341cb5c0ea73797eadbcd3c5feb146ff59770ae7cf5f674ce777939dfaa7368c61b6f35eb33f5fdcee5aa73b99e24b75b940a92aac83d267c537d7b3affa6b5be146f9d99ad8e6edde4d559dcbf993b2fdeea4dfffc37ffcdf2b75eab94b7a727c9bfd0d0bf994333d6eb49a238c5fb775eea4179188946e75523ab530edd6ee5d07ea41e22abd32449506c867379bb2b9177eb1eab139bad25cee59d53ea89cdd62f33da139bad5627497aa1fcab493efa735f663497774e97583501052680316108138c4c68b97b8c8b268c60827577157272707265796fb76ca243383dafa63c49743a9554ff7399d567c16a6c4dac530e99434d4fc4a4e7d5344959e2c422388ade10630ab1a801456ad10945406291acc808911aee7e5d24fab93b0a2e127900050b2853e85c8442054aeac9049e20b9bb015c74e285e8640b15179dbc93149cd0947001773fe262094194a043cec512622570a0d3e95ce0e1b1d65a0158dbb3637374eccddeacb53a3d4949d42c54646fd65a9cb2bc16355538cf4bde6e290a20bc9a511ed6d97a55753addf8becbe47b24525ae2a57f25149ff65a7dcb5752afcf6a6c757a5bfd52fbd46caaf767e45d1dc58200c2083464ad8af5ab6579edb53abd3efbbcbb9f89ceee8cead26375ff91bde9fe858e7ae47dd26a25f2aadbeaf4df89bde9ce33ff94e82a44aad9fcedca9b4e9ff8ee072ad15528a7b3fabceb0a6fed4da7b33b4816041046abb1bda4d646ca6b7f67669b776e67cc1b6848af3abb332669230b02082369f72dd1ebb3d7e6bdeeb0bad14c6aa67c9e38a6acfe58104aace6c85c89733953e39e7268eb6cf4b3d13771aec4b9ad735ae25c9a237f4ae5d04f8973e8b3803726d12f4bdc43997259fec6e5e7f4efbc94ae7acde55089ae4247e5abb75b1253469bb49a58265d30698109132634a138849a105241a889bb8fa36ebfcea97f96e8ed5934ebac55bafb768b5262201d8e1d71565f89aad66577727ea7db8f948416e970ac6e48a72b3356dfc8b7fa308aa1e87ebbdfa5bebbf5dfdfdd9eceebdb4bcd3d6447f553fb249a95f2128ac7f7e15da26b567390ac8e149c263d7d7bfb79eaddffaa99b7cef9b26a06f1e0ee79af41a9a36b90557fdf13bda4dd18c7ea55039960bfc60175b151ec03c241cd5bbf9a8e402d2f1f48c4632c9fe9f6f68f14ee23ee878751fc692affa7898f77f793814f17ee3e6a9d4fbbd76c511e8a758082f6bad3b9c7c4c4459f261f73f4bf69519fd69863d56ce288380decbe74e222ee8a8f252e5f25ad8e4e8762ab2fd3aa35b6e7dd39098764891ce96fdbb244b1e5a9d78e68265fe7bdb58a4d7189963127eff297b20fc796fa32212d6962b229cf92f2cc4dca33f746179788e5fb2e2a92e0c5cfcb74d5b2b7714841b51a6993bd6ab6db2a6b250d56f6682e99ce5190ad152bcd7ffdbbae4c4696b212c54ce89ad15ccd7533218816235fad1206235fb49791b2d487613ba3a0d7ad5dd2067b957f61bb57cdcc263a4462f41a8d238d884ace2c9ad16ab5176d56a3b56c339bab4583cd6ce68d992d1af9e63fac6cd568bf7ba61a0b1d1589ed888266b25dcc84d560b618599ae5b760aed90b4ad095916469daaeb9a33d49bac81e2da7f1cb654bf22408f6dad5fe96af6bda66b5d8ef7a34d7ac447ddf4bafca93b95e2e9a93a059b97bbd60a44df6bab2b2d5b2f568aeddf439debafb32377bbd344dbd3da412825ae48b7669bb16cc35db91bbebead15cb9160c37145423cb59aba4d95ed7e58ad968250fc54ab65693a02763b1375bb572679aaf1959ebd15ce5336555cdbb5ceef53acf2763362641b4da8b26fb198db4d15cad9a8b9c1151c9a5201444735d7267dac8d6256131daae368ee7f9646b46442577425090ad84bd4cd98edc9935d9cc65c27a345789739ffef60fc58de30fbd49331811955c105010e972c162b2d8ab268bb568f715ebd15c32117e826833b266d2662dd8db62b417cd7cc156359b455aaff54c7db825f3ad286cfbcc147c82cc168db47d6d167b99e57dc9be481293224de55091a42745c6711c759a5d34222ab91217f42a61a6cc65ce6033b37cc95c36264b826a7757c2ca277ff7fa566bb7bb3dd94bdd291212826a7f6b258c56c6c8bb8395e5ae47733d89e6dc795ed2f6245a840acd6a3acdb79ea0984de6b2cd760f7b5d17496bb57a3417084a62bb9fb5622d5366db9131574973b94a4852ce5c3f7b5ae9723d7965b74773c15c4f9094b19849ba6eadf5bb96eb457bb99cdc82cc16ad457b5dd797dfb2d1ca6b73d56c41e45f7247abdddaaed6b299335b8fe62af3ad1634db912dd78e466bb95a30f26b6599694135d975c964b5173973c5fef572b55eb65990cd6cc56cb219edba622eb345963d9aeb4c7d3857a3c9825a5fc26ab056cb352363ff72d95e2f24b1a018796d2df24b59ad56ce642f574fd366b02057ec557b952d9366be5a252de6cabeaca6aed7edd52a4b1a39b3c15cb1f263325b8fe64ab35aba4a7005bd66e597b0d2467e3923612eb3477391bf94cb1bf66ac55c2d18ac4673c15a2e19cc5583bd643057f934d27cd55a2dd356db7d8fe67a349f648ec4fb6efd72e9dfbf9d8c884ace094f506bf6b39aab76672e72776b329b6d464425b71b8364b42b83d17634578d8c993b59398ee738c26453768260ae1d4dd67a99b2f2b6c8162dd6a3b97230d7565fa378367391bf041bd22169b416ad2cefae3573c166ad1ecda5de1ed972fd46b830d92c668bc52e8c84c95a36b34773a95bd7644454724c47826a3bb32c63f7be62bb164983b97a3497fa288a732fd76cfb4cd82cc79773ee2cd55c6e0627c8269bbd6ab672276b91f76336578fe6eae935cda9d99cb99e1809225fb597ec63b6568d26abb94ab34773e5683511825e65e982d96ce6ac45d2bee5b2f568ae9a8b164250297395b259f97ac9c8db72d56e8fe662ca2a2f67ba5e45826cb7345d25496bbdab2693b96e8fe62ad19bfb25f55d2f5a9124264570457e8a2415497a52847c294cf7c8e6a2c988a8e44e00c115dbb9aecb7cb265bbb11bdbc178ebee81f42dd27aad39988c884aeebc213f568b7dcb9cc162b58fd56ae338be6226115b2df6326db2b2d6dac12e59ce7a345791242645c671fbcc717c359345929e14c9a1389766b5a4c1d49d5e5b7bd16430da4b46a3b95eae19cd157bb95ca36fe93cc7a01aacd5bab498ad65fb566c06237b34d7f69939db6fac4e916d9f097b359331222ab9123ec8e955cde536f0a08321435e1c6c40d3000421197c80c105aed8ac83201e5850c14cf6b2a9bd5ca2eb0648073f582b23351ad0bc76ce3347541bc8b8721ea47831289cd34dc801d8f443fc87035610f0027cdf45512b08f4b1aaf12e5c0481f32a3d9ef925b9bbba5c00544801052a2700608a9428494847261815114179e2e2063eb8c8c10a2754408d77d1c2069cbf96723637526c6e9ad8dcd03cc7deac8ebde194f7d5543f0b58e762b3d57c95480ebe922f45e314a7717a79e899fa701253feb13a78aacf7b75490621055df5af1a2493c96432992c168bc562b1582c1683c16030180c0683c15eafd7ebf57abd5e2f97cbe572b95c2e97abf56abd5aafd6abf56abd5aafd6abf56abd6c369bcd66b3d96cb65aad56abd56ab55a8d46a3d168341a8d469bcd66b3d96c369bcd6432994c2693c964b2582c168bc562b1580c0683c16030180c067bbd5eafd7ebf57abd5c2e97cbe572b95aad56abd56ab55a2d97cd66b3d96c369bcd56abd56ab55aad56abd168341a8d46a3d168b3d96c369bcd66b3994c2693c96432994c168bc562b1582c168bc16030180c0683c160afd7ebf57abd5eaf97cbe572b95c2e97abe5b2d56833590cf67241f10df909ee8edde68208b8bb56c7ab3e0f0fa9eaf33ed2ab46381b0b56701b0b5a6e53c11dee9e817f9081cda6020db84d0509709b0aa6b84d0535b7a9e087dbd8c8e136365eb88d4d136e635383dbd81cb98dcdcbddcf4be627434faa6b46fa6d2c2513a9e5f529afcf1fe9de56b14f797dfebc1be93cefcf6fe399ec9599925ee99e3e258acd9387f55822c1a3d5f777f7b4fa32e1beb755bcb3d7a2e5ddf5cc9cdf458776466d86b88304700707e00ebad0823b280077900577f0d7a442109521a41c501050d10f5150eea8e8e8282817f4240a5011941c007050f6e5dd34abe993a5e7a1f9ece1dd24a3dadae3fb3d94847c299a97cf72e851cc43837e80e80059790e0ee80e3a717fd51d2c6108d4f20367c743aaa6da8449f92b58241bbd28f66195d4b8b4be157d9fbe4c363749b5142740fabcfb9a67cebbdf55ca3d5627c9ddb7b459e181b31428afc666b2d9d525f92ac4caa14f7f49bf779277fa53c9dbedd517ac9c0268e71329365bf5cea7def944aa09e2697a7f5de1a645e5d36890a6f776b3b964b297aa8d6602b0daa8248077b5972ba7d54fcfeff1689592dacb15a5fc262cb4519cd35cf94dd8bcdd6e2ed53db119ee4952be5ef7dd3236bfb394f37e9ae27e672940b91c08733986e232d8c4bc6113d3725eccd6ead8db37e5f96df0e5ee28d0a58fac8ff7daf6f64ded79e26e377710541b14128e071e78e0810409223151d89c7436211e3620113660101b0fa18f1e7c267b35c9f9b09a7e997f401ff800e35e86c0071677e743e5c30477ab2489b5379daab35bdf74a9bc2f2928ce2525779482271c49c86808ff68331d42da99e8d35fd2a3790f0fec11848fe51eeab85fe7f6d0f191488f2abc647a223d86b8bbd75eae95d418cabf9046810a574ef08418a60c17c364e16218295c0cd3848b6122e0621807b81806012e8681c1c530aa985a8551c1c5304e5c0c93c4c530382e86b9c0c5302717c18471114c1b2e8209c3453055b80866022e8201c2453037b808060617c1f85c04130017c1387111cccd4530465c0493818b60542ee611c6c53c3ce0621e60b898071720155c50c1056290152b0fd1c52f3a8816dc81c5c53b985cbca3fcb2c5c52f41b8f845872f30b8f865bbf8250517bf3471f10b092e7e79b9f865888b5f7eb868471817ed78c3453bca70d10e0bb868c7131bb88d90190c34b061d9a1818b75f0e1621d71b85887152ed6b1838b75347170c4fd0351a46374918e1b17e9b0c0453a542ece210217e778c3c539c870718e2d2ece71848b73f80024830a2ea8409c638818829f8b2138c1c510f8b81802978b21c0c0c510a45c94c3042eca21878b72a4e1a21c5edcbd830c38f8e12b0b7c033996b82887cb45393c70510ed0c538e270310e0db8abffbb4f32913b38c2117fded5fa7dee608ebb8346dcdd411c77078d9c676e8246419f85ac0aedd71a87326554098dc294cff7f99d68912ed155a84b1e2ff71d01b8380219768650a6ab0269f5ee0bf46a263590ef12a1f7a194e89a8fd021cd7455a07268a3afb3d1cf67e3f7e1241e8a75364a6af2250df739ec1861083abe20492987dcbd26d291c4a988738c1e0031040c7006c8f1c44f00823be2c072bf8ca3041028c07d2d5fb528b65148cccb4fdc3d0f5004f767016b6c411938b8b87b92f2cfbbaf903b580168e3ce43b13b28de7d81d0f0f08c3722e05e0ea9af6652abe34ca3d90d17dc38c147fdaafd34a7749f49e9fe14282f8466fc8245f933c56a524e1ada8ffe93f3c409eda62762ca44ff42bf7732b49b9e4893e86e7a22a42174a80d3a468d668dd75cdadf0929a999097d9d7ba455d77c821df59778695ff5a6e93d72c4bc4cef430206b2e3121db02eb2d1040634830355b8fb05c1e000166b149ac081260e74020756e3940d80717d1969680329f812de80751f4ba225fca4c43efc84a83ccffb838be2493d7d9bdc6e24561f7d1e93e7a9b7f7b3060dfbf51b70a000fce1ee204b0f3596dcbd95dead7d78a791a661dd3d8b14eca187bba3e00453b2890efd0b6940068d3c7441a59ac93fca267a4a020d0cd0e84144830577b7393abf57c239430cbbafb5bf573ae38c26eef67686cade320086db5b06b06400c8ddde328012cde8c2edcd0c05d85b0af66646117b2ba30e777b2ba30ab7b732b0b8db9b58069048061eeef646c61517c90832061bf6364610eef63646cd5d3ff6e2c5077bf3a2f3a2636f6280b13731b0b8bb1849f626862147184bd85b184f765c0c230806da70b737d0070ca4b0038609dcc7df4e7d3aa3f68666acf79aa2d896d7078cd5177b902318c1084690c508c897a24375ae9a0dc1ea4c73dd591e8a4bf59e17abbfafcfea6bf526afaba61fc9929ef3c28bfbeb8b444403770741c8bbe845edf74ab9f392190109e8220af7d8e81a5d3be7ddd9d949f169e67e49cd6934a34d7bd4a1b22e227077232e6e2187eb763e917414d8220577df2942d44203ae050deea4f5fd1ef2e58dde9e512e676f597d2a4d49aa1aa454149474d5f55e75bd5d8e2035f6f1901afbc42c88d0bf6a8d797ae46f5f26c7dddadb9867f74d2d925fc21a7df34b7bb3c0210b1c726c3ce5ee360b94bb6b6c8dae9d26ecdb79f36a7fc33d57a2ab9005f0b000f9474e989ea82cf30f17eceee389b3fa54d04cf4038466a2a09c34949374f94f447e8af4be4b447e8ab4e50bf7dc7962a55c2e97deefe5f6eb2d3f8c6a3224a411c5d84457a68c2ad951bd27f9a5fd6df5d3aca6f646a826d3fae6c97ba6afd6644848b67cfd527640e325addedd7dcb9ebe4db656f1fae59971a0d851db12bd37ad45b0faf6d2957cddabbd6d75367746ad896d0e8e7d7d5e213bea5eb055a7cbda96ab9a64f53dc7adf3a97d57eb74ba1c1d1c5d4fbffafb4dab1ffb34466f8ffcc7eab6fa97fec427b9b37b3c7c5a4de2adf179e29298d9de1cfbdbbd7e357d15c7da0bc58eba9ea424bef3c45bdb47f3fbb236f3ce5eb3cdbcbc6d53f6b1e3b380f50e8aedf3d47bdacb02d6564a3632514cda3d645174b53a766c62479d0ec98ee5a73977df326f6d24bd3e1c6b477da1e8ec353d6f0e8a716c6ff3d09ded083444aea6fd5e528c0b4e6ce1ee6e7ca0a76f4fafeacac3265a66a2158adc9111144aa0684191038585a2068a1928549e669b13028ebd591e9b1382b53a21d85b9aad4e0816c7da9b8e5501852f33f904194f48e1eef69683139eb1a8e4ed6eb7a91ee51c43ca8cc88c8c040000c313003038281a0dc7e301b1306d61f114800367b86c8642940a064212c328ca186308018a0000000000081898a9c90edee73fa58adc62b99f52b48bd185ea3da0876d599d3934e15230989d1eb8516e04ed3b47f98a68efdb62883c7afcf11fcdb9d026015519428274d56280c3b2ee70e405cb591df016c74585b1f4d2c8c31c6808a610bc4f57caa49d0a969975e73127ed12e82bb75f3ef285677a00c4a880758389ecdef0e7697df3bac6ae34631fa1fd37ee45906bb38bd757006fa1a09b5f3af278d4fe27ff05ecf4c97a806d867d2f1a3965b3fccbc3ea725e95e6270fed057868bae1988e3a5aa2d118385b206e19cc5cd1464a2388ae999b202a80cd4bf03c8e150818fc48728c2dc424fcdffd375016146fc89cb8885b3eb29e27507ba27000e3bee8e4eb3a0903ad09de57b1e66179104a1a4844b44ea74e412f51a7c7b1325d11f7c4a3a1e6d90bd574f27b021c73e7ad9212b265f202a0a06f308a88c3a86f80f0f3b2f32e9a65632f2e8e772921e7723748f5e80bb0bcd340a0a1085d580b3e76b0fcff2724cc1a999a36cac7b30b70d6681b1de9ab84425ee5f0eded70ab3880a3c6b6cfaed39aec83b6713dc2d031f8ee14ce794b5e222324a4df0213e582122ce52b49bf1040e347a8059e1f32e19aae5f0a0a14f1a70383576c4169b91801178608b73196629c87f8168a54fa5734af4743570354024480dd8cc45029a1a9af4891341efa1049556ba6e88bc8b1ec92bc96a3e810fab2e566025bba699dc366e746459418814a2c333d5dffd4810bec776ed25bc9045ddeea776c70d106c7c7d5aff0a3edc44087543de0afc3f665848b2c79f5eb3292b6eed57fd2ab0b2a4319314453ff850d587f177160a4c1fa5de6dd11a62b32523288f55fb3bcff2ad66f49997d00a52aa41c7cf5eb60f1c470b8be4f05064e59bf5b1dfce0b2fe1e5e58bf4e1885cd99f5f77f0cfb38ebd76a5fb052f5594122d82e93288ed67f7dff2030ad3fee1a6930279f1b2ce8a8e9c753fd0ec2d6fa89c588e5c0d6ef0c8f9f94adbf3f082a9cdafa29586082b7f5ab1feb21157329e6e08d8da3d8d52dc1bfa92a8be595228d596319ff592992518b7c76c1cedbe8c9e9d90a885c0b847223b9706227886b0eb92c0b4af825740e1430096900dfef75c8cd4a828c430b8dfcd3d3c4c88013ffe1aad9e070c110efc3cfa5fbfb8d3d24a9306f9e39a0446e079624c92b6f8aaa0d3a9054a2674fe762a716b94f63531481e8ba1a95be0bd8bdf884a7e757f9d8e721aee177b4f08ee8309815cb9e27a858d24738cfc0a72599ede241f5414ae94136ac7922c605fdd44e30f40e9ecffdd2a1b19ff22fa975e033f099540ea9892c1632974b1dabd863ee6f4c31e632a9b45c2e9579ac41feb9dfa872c865d2e6fa54203ed774a856444e25b80b3ba40f93ca25914a64662571f86b11fa0dfe66a597240e9b4acbe552cf22f435f793972e895c2695969b4b3bcad06bf6372b5d127359147a2e977a16a1d7ecdf9c644de432a9b45c2ef5497504b8bcaa255fdc366dcba40d49b6a3d838b9b6cc5ab2d590a43621ec252d7c35b46bb1ac4aac953577d6372307b26a81bebcd164e39c02ce568bb57206a3d6b37a187625d0cb198df5ce96a218e4502fdf6cd439a88d6493c17b599341eba81637af0c619e9f384930466d436a7e5e3b059bcc312696898715fbe5c914a9e1630ada2a6757951020f30d418a1be7f92b4260c9a6fb19abbbb7be918cc26854f59341431cb404fe5d166f5c7a4116227ce29545d7c479b2444c2617efe462dd8692bde763e2b869acdde4229f7963a79cc102e1289f4c808fb09a0ab2b58c60e5c6f0de4e8bb2bf65a4428916fa7604c92c594b980d46f54afed0ee46355d1cfed9e41cb68819dca26cdb1955f9a9e73e80c1564f824d0547eef0441d1eb44aef0e8407f1adc549642eb10898cf30451eaf5ae49640049cc17f1cfc7734765accc55ca1c2796adfae4539766be579c9079db01224080d3a8e55a6a5c003ff14a818fb1e86450d1df4324f7bc6908ab6fb7448c379773d1a9bef5d08222633d59a2807655d2ca08ca601b9c55e7c8aed808987924377619241f365b56a9bc8e8d009a4a8badd93b3af014d2e876314213875b0d2cd0189c6c606ffc59359d669bf0a134248d5143ef0595501854d0118c2f2c669b98aa465401b139c7c8c98604d99572dbd92dd9b9300f8183f8238f9b89fc6c97b269a7d54c6bbe326186a0c520a6662819b68ba6a3b26e6c3b7e0ccb47d7d14779cf6eb80076ced070dda274d350cfe9dbdf1b515ba1794ab9eec59ec7e940182c14298c49c769f205ebf3e3e973743be672dcd53d11c819ed378f7cfc2c14cd85f53fa896fc72a372893f093dc627a1c9afc85c4dc22a8d14df43215e6c000374f4e4f7289080785f44875005bbb2481de588efd5a5c90fd32987e00abd9ac7b12e446d21b8c7506ef26aed4b705a9a98010a614f15782d1b93eb5f0fd9a42e71a7c432800719ea9180702acffc22197658d325227a0b354efdeb58a1b5ff631bba4acf9e1ab6380fc7922217ba76dc3f750dc0505f2a0b479339ed8b344e5806edf2db3549f319057f3bb7ea211906bcdf3ca04d8498b7a245f7982f0a1f6d1a7d8f8027bc14d98177a78e54bb26c1a31753bfd35b968eda5166fd9a6523b9f2ad4876c518da9740a6463b3c2388a85b2a382f9f6e1f131f6ade48a9a13dff62e418669aa64faf2bdb5006933a991242e8976972cba4b8faf36f294169e41b4e767ea2b1215991c3097a6ab60ebcf67d41738cbda21c73446f3dc69ac79a947d48769fe3a16f921c711fc26ca4e4d5cfee72e7f0c4c6a703e0e1906c53c2031a93370fb58d479d6f8292f1512b159e187b05bbe71e59acbea6e00021ba09ea478664097d9354caf8dd518374f7b98bf1b0b02353abf13a09f841f11df78b245929d5457a70e7b7bd3c3c553ec3fefa423b1836933ee138ce5d2fadb0fe3e8c031f88bae1974ce1637f7c27f862d6397df62285a03d6806c14b78e2e75eabea03c378c47681bab2e9aeb8db3ff217f8f23f4417e7e255e09d85bc64b303f3fbbf81a83efbe92c6b68995a90f7470baaa4a7c0949bb09be4629c995866f31d1f88aaecdea9e86e142a1ae7a240571912533dd2c1007f6ad9daabf4dd2599c34404311842dd7f45f0ee962b1a679deec972937078bfa74e143348640694a54c14d6aa98cf3963a518813718ae3731a8977c0f9d5c4161a9d41e990d34ad7c293d55b5e72354f28382d55ce92a517bba320c911ad504be025f218c49f34cbdab657ae2b9e3fa4283650ca6f4f7472077d0288b3cf6f4712fcb8e3a0f9c1ff99c44d58024f04fcf13ab473b8f8e2d5174978e1e1c82f275ccd103f6337e93bfda31bf0eb9ce1f4adde9bf0cef10447f0d5e81b2322264b31fb73f42940b9c94ada365b50c8638043e7eea23586df0e634d0eb56c305638cb6503aa87efe2ad2520658ebf1b031c927b34514ff379d73ed23d02ea476cc116ed83cdea29fbf591eef5909e7908af0ccbb24ddcc5732ed898deb6dbe39e806f940eae87218356a1ae05d40b6f20fbc1bd8e2051821c79bd92ca868c670f1c531e84c924dd3ee5a3b8044b5f0368be1875fbf55b92761ad5cf207474dad936ab3b5947131605139ae61d535102afa7662755470d5aab09edabc8d63324ddbea558d42a0ad200ca5096a29f9920d4a49eb0152520b434c0bbcbaf3c4a101302bd902026a2b26bd88e7c911009920870ecf0a63d0840102d1e6626e23146ac9b27f58452a3004406614405a28c1f46d4561d8d95d165e3193941435f86f8cdb637364a519d6a26218dba7b03881a4b139d1c80d3fefb294bce46d489e4d6f3e074c9fe2c1cc0bcb2ced87c3d81594b1d8cc346150e51228d82c2ae2c4b950929ce57193cca593bb6139bd8a2720ee7acfd92675b9e30bc92b16c75b4011e72d9014edf9f852d1509ff09541335cbc0d0575654e76421142bcbf797acd1649131f4f11c0df9971ce071e8580dfac79d263db89edbf34e8910a1cb7083f5a6eb47a5db761c0bd3847341e2acf2ddee996e885d1d47f0e9f275e0a1fa581754367d4237fa932e3a4857ff0cf3aa7e4f8a958516486a5e0d027b7a5c3f79bd621ec90083d138e49c0739664233fda864c8a72902356ec96c9ae63f373596098f2853d0de23224fa0cec228f57d7db99a41adf79609722a1870fd8627532c5b708360e42da7858e2e7b4b6804f4bdf42b25c061a9b127d1e37645d3dff31186aaf30909d74154160e3946ac779796d37a5ef3902e9e77f794284d1c14f7c03ab7c333fa4feb5babbb335ded71a0a1c69e0b4edc3815397452f91b5d0cd7f434c23cfe4b6dad86c86c9c5b77d4278bb3d7042453c3a65cbb1531a8a2190402593ec4c71cab55c280959b82ae73b29b79b1ba4d688cb55dc5d84e7ba4b241869c1622e6e4a5631289471a7bba91e236e2d6d41d4d858295a5a001b4d135359b11fe62e52d394c8d20ab1b57db5ea0503ba8a968bb40ca3222119e29095aead6faa827f95a801495c16bf0f58f8d644c99af66aa6004bf20ca4151d796af51d2f2ca53e64e64ada07fc795f4215a4955601fa9b9042ffa776ff159ab670770ef5c2031e298a48dcc8d34df42f18f41794e3ce21e25c933e030aeeaf17060353ceea0d1d4e2558851879cd5db864c20f9121ee8f9afeae6227033457bc11c2443e6c89e68de22d3612d068746af73215d2732b3755b6b813ebbebb37aae67e6fc8da4a0dc9faa93592e9d2d35d1f10d87bc6472521691567609cbc8689e1aa1079081814a0c8ad36dca979b5e79b633f8462d7487e8abcd9178f3cbfbcfa4b40a59f70b29ce00ec1a8850b9ef6e0dd007000f5b7d392f53eca6442d149564d385488dae40f86d6924aef3f8c3fbbed37b81b6162b085b3d8dd003843faa07f7e91d1293f213f46467176eb108a6d91d99b9610c304c72199af3d262a59171406a0e583407ce52b3458edccf72fda05d3a933cb8f0df8466593419b27a8e47ed31f0ad146ca465a1290e067fcaf2f48b35a9b541f64095c8a427325830ff2b36fa0482caa6808cb0feaf97517258fe464a4adb97decc55cda4547a4d3da47a55a923c341160f8a82d5fb2d20ed5faedbd21759b5ebab35dffa17cfaa27edfb6898012c4751047104df96f8076e6d3b2f9eb52372447d42957e3996fe339525e65c1b79796144ee3a2a34b053bce16da387ef9ab6a4abfbf9defaef106f6c053bfafc12b8d5e7a2ce3e17344a3c9937022f6067e4010b4b123e144a5849633b870472bc02222759cf49e894926655095c60cecb30bdf59ac5189e4f6386dbc07bbd3ad0bcf3a3c1df9b6a7a7d6d939ea1d27a6892ef46494f57ec2c00b2900f8aab44b282fda705b5188a0cd91b18efbfad61cb33ec48b0639815a7b8254d6a82d0301668a537c66fc0a7df836ef57ee28f7c281a1b23f6674b910343241daba0f1512aa8dae9af78a0fb9054b802d0234e16e90bd6f806f6e358f3686c561f35561ac5af85678cb9df8297e29ceeb1ed0778ca8b25c271d918d3fdd393a5866feeca64979e5396b0ae6e1908d8a5f806e2c69925929f22b9e6f7e7ca40d35683de30f9de17caaa7f8d637d976d79f4e6539d1d7ac90f65494aae8bfbff85bebbb2873fffb2e34651c93f67af145281689df5c667faf92fdc73240f846ecfe9c4a55516deac4da0a6ec21caa41fc8715afd57697de337a6fcacf91919230ece89dea60c0ee6150594bc695beb0c60677c1d45ff81efc82fcf7bcb74f35bdd03e5d45d2b552b74257356325d0b0ceebf3f67a5905454cc3187b5bd8bf03bb941ca4d929d853c221833167c7ebb24e6967acb5e6bf7b9adddfa26f37d4f1dca76d31ae160acf370c3f0c20e8cd397ff63fef83a9ca113a8ec3efc48c7a744fc3f0923484fa0c67642ff9766f53e085685049df815f9d6a9820b779619ad0e125a98331f63838e9bf89206aed425ab240b0c99d65894c9d422b592ef5a227f14fbebf7c6b88d8d369a7d37ed8ccc8f5c80f8440a2c2281edeb009cdabebd8db07ccc98bc9f4a47514d5ddfd987a42918a9f8bd321b91ee054d99fadf14b846e1dd1b26a04fafc3097b257ab2e09a9173b623bce2a91a1e016eb912981e43543648ebc5e213e84eef4e6459ce8b5de502789c27225ea4efa3de21b9a2acd3233737b2649cb94ea780dbdcf97667c20c6bdbfa5cadd3c2cd37a5bcd5b7de727ea62c01337f6f9260c1464798e1d4b9b582458eb05f72d7901f8cda6a262630c8a11185e016b3acfb45ee63bf2f15f28e5421d880b9e401e715110fef0d2765568c668f462b956dbf169954126da1fc641e28da00c57f69eb473d758b5028efd5630828db8187fb7790ba99e2039d178a995dada897a0f792e61f4ae251dbd7b1a18330be2cdad4fa92d2953e86f9fe25664f1e91ebb48d489aec62edfe4061e47c9b535ed9c2c82474535d357a19e4c6bbeef2e7db6790a06b33cc79d98d86ae3fe3a750821bf4ccdfb8ff105a319a6ca4d4090635fdc003cd49ed3d18ea63c70709ca3cc0525ec5edaf2c0ffee699fa0efcc7f0f7cbaecc353c731a66dec83c8c8509f3a4b0c687d3528d1c87caeea2f077d95b01590f808d6a90642ade5c3ede13ea8c99710b2db320b243815c437109a67190344d0ba11095a48259f8d7db1a76849f0ca6de26519995ada9bc48fafc0dabfb0f1fe7ca8541b2ebaac202df800dc63a29f620b879e661c5938ed6b44778dabbe056c74c17fdc8339c4905f0a9cd8ab3f60bc635bd28c5400a8d58dd43c7674405a2ac058d29d61c1fb029c64d6d150d54d220026d82d1f90e0b2f7645f7e00714a0b6edb3a6747c88f9c1c507a946dae8b50203365d312ae48e03c72f0667cef40516912acc94f37fc58c49c67d5c6ec3d29fdc87d06ab86ea41f043daab5e824dbf196474f73c66f030fc351f51aa2e40c76c4870b77629434c39c6864868f219c3c667702ccac7771aff13b3c333d486392bde4fe179654085020ac9f64977e79a300309810b6d95a25ccaee6b1bf53d62d522bfc68580d59755b5abf6b127233e55452200d6ecb8715e8680142ee146f261f0fc0ae54975a7c2d37f09ba192ab9ab79e86e49da0df799184612d68d0ec30a6128d243590754e0574d2cabe1efbcb32140e045beeb483bb27a2ca0860acb4a8a226543a01ab007b3eeafbcc92c63a13e8c8d3a0d3d3a25b9f4579126da3b452e80ce6731ff804e7deba6eb9907207c0e51ddace6652c2a0b8a3f46c5b48a4c511d3aa53739c45b8be38b2783e1a06768ad17249b4c252fc5008733d882223eafdc2c38d52a1ade4683d9e397cf997ce7b82d0445332abd2a41205054ed5a5d5f2c558cef8b7845a41023769aa25e597fe32bedf466b0d0c55b86a99ac6a37c26dd617e3e87676b525f4c25bb8b85e840077bca3705c16c8da44d782d9d5447d5dc0902e408295608564063ca5430289a214c3dc567e0e4aa56c90c4050d1628e5251628410bf36dafced5f9889f93472c9883ac47af61be8d8c72fe44a7548fdbec39227ae1796da8b01e3bba64544694db408243e6beedab5e338a66f791f3a45c0912267548e79065379e28a4a10a1a3b9a1c2caacbff09ae8949843fc43a1ac1d4416d2baa86666c1eb0044a5896d9359a0d54e2b84074674b5a03a911fa5a2a938574363d2454926a813fda70c329c76c3ba4ead21ac3578c7bb043f17f77aae054f202ec68f5a0d46f03d4124e7e79d83d80b52de743675a1e128e3d8399db2a0172af60b6755c23799b8dd3574af0441a86ecac86a72072f156770b492f28ec5a22996e2405b2cb44e3b13caebda517d7096a60f62ed38861d943dc4b8868097ffecf49fa97dd58732479de8514a455cb1d948a2302759f39c0e6d2d1f2f4c0fdbe63603fdf62d8f2052437cb17f694fd39e67fc08a71a77692f6a49ed067d656b9bc816f67f5a063c5e7ea4cf20c89dd0d21972e09feb5b7c7d49eace6501a65d44aaf4684e203d82fed531ceda465e7d430d647bb6c29705dde593b6a3113b91aa2c6f8dd9bd32fb3cb6ea5e86fba217679958747e7f318f67b3beb6775c82af44c8e33426215106f1e1ea406601ccafa0833eead0863a79f974146967f3ba31918eb2c48c243ede2f85123c61d7e9c764c2db04d41904dc65eddcf3f6fa55fcd401633160972c8181f7acbd7f8e6266bad78109803296248f38fcc2699156c4f5b672363eb5491d66677577ab93a11f04f53fbbf1ab4fc990e6671c0f537fae7125e1c5c96578ad50250b2eb31381228c6930c5533e77cf03c1e3c7f7992da9ad803cbca834eb3160c4f1599dfbfd7032a0557c21855efbdca25ac48d907cfd3ee8d6284a2da54c1791ffa4e8df7a907fe2d541981ef1ebb6ab7049c43d75c0e533250849327379e019ab3469a902bdc653302dcc209b39a0be6aa291c59899d6467e19be1990527260fa2f8ff3e9bd25694c4943e13e894c21e5ef73daefb09cb6e983cd22a7bb4ac67f38404257580522618cb4c81f6622320316de5c77ea200edf178960f10c310095213ebf43126e69b485e3e969ae0c8c61a65698e0ed2bc23190b676151d98a70b01915b6745b28652fcc460c228aacc24258305edcdc7df1e8eee20e3e1d8e390c7f1608050c98b603db57b0da0812dd17ca11709a246a49dc8078034dc9d10116e9d739ba78cfdbbd03f61cc00418f0bacb58a29bb779cf040b936d40c28f7e585df5accc4d3b73dbaea931f915b4e27dd6d707a571f09a5201aa8bbd7a06c5bf3b2d41ba3733e9251364ef280a02491e4c701479c6e07447b7511e29eb18a36753cf822a95aab33141cc49f502025e6cdde1c799289e44eb13aaee340985d1b84592dcafb5cb7e430baad5d476da30e0c18ad58e0c950ab4ea15a93b7c34cfc7fda306b873b7abc719011275b4264e94c27ff235b51518373957ed3a34cf60183af171468fe82d4e05084341d123abe16c196939b9de032efb8c98dcf0c9e6a73065970157ff76f6da9c33a130007255a2391d5d45fb0135eaaa7c999a9ad136ba36d9c62e6ad4554a16b432dce95ea6aa8f554fdeb734a9afacae9ce68a942bce170907a0717294201b32ab9b94def91dfb7a2b11ce79fffa57facc0be2cd6a62074d9a12262d61ac2002c56c83188291ad2dc898550a361021d30a79d903805d2daf2883088ac996786d46a81905269bcd2f1f22c52ec40be0a75f668473542069799c148ed5ba610599f7321670b1e18345c17fe567a3a898f1ccf7b07ba06504de423f32c0d605a9a855b6a9327c69f27b822d96247edfaff10985b24a238d297bdc080d074b53a5e0f03c694869090ce040cbef2202c01f61b91ecc3745a50a4d3b55d6067dce8019634ae5d6f37604d323c805ab237f5f1537cb9f8f6a7e0ce37178cfbc8be306621b8a749d27fea977475b8f6f0d169ef89e51442ae3e790a2d8876f541436fd1e48fc6cf2a54b003fae7599a7e4b738d7ff1588eeb4f394a162d6dbd696bcc9fb9b56b76e2c8be13ae080a560d34e7e2072d8c2e925da0b1af6ab9fcf28d1bb31fbff83912812c06c516a4a4cf37698184fd42e518daf73c4f1eb38155b8d4c9e99b66bdbf27a7c611352780913500adc269953f7648697575c8d39a8844c04f3b2c89b64e53a6d17a6eac9f926931c5d812cea28118f646b5633dd38d2e10513a097f3388e450937fdb22a5ca363a3f3baf7116e7b82f358b84720d6fb66c68546327c42308a748830d472c6a23b241012ca356a70cee7c0f910eda1a64bc6ef8d21ce4e6f019b4a37246c07a72a1abcd895635ee6a4cb176069ce70b4dbe26c970de30e1aca098861642cdd10263850c5156d4b8d1cc13690859c8074db4f518ac4819a287e502381609e32dd4d444a0c0a76874f0097225d465111f200721409065ef532b9e980b16725e6b1fe920389e9876731c84466ac6f3616942fd53d3b98bf9a079a40eb14e19e7b215edefb37ca877df8a89d14b79077df29598140c017d44f5baf406ced4811e31e34ae9e4257300f60530dd8242d6164c2f80e1e4db743b906921b85f9647a16080e79b5690477957d58513a7b435a6da16edb79d3937c3dad1f1a8cba291b07c2dfa3ccd939ff93bd7d2675e5eb087374136b2b8baa88b60dd27a20d19411a65067a1ceb4a09ff8dcf018cb6e389012837ad6e8c017c75c245cf65f02dc1d348af49046733c517e8d62aca03277a10a0e4a9c4fe9cf822aedc48baded0262e48648b1bfed15ef82430bcc3ae20572ed2f66953508d8224015979c05148d91d3fe5b89c7af45e9019d09072fe6fb989b0d5a81a89d5ca142a8314d1aaf959ef3cabc9841fc59ba9fd72122a016586673758a63b8365dd3c29a1de86bef049962f02443e211fd5c4ab747c1efe37c696791cc3198766ed5cc624dffeaae62ec419342c42f56b080d6b906fb465147ce7948a20f4572c79ca1d307aeb20f3b82400839e3ba692dc8718005b0fd2d8466599c18f42b3fcb201cfdf0e1dff0159983f65e5ffc8ed5887020babbcf6ad4160009d7102439b73a4bc608ed54ec63edf3c8016a01c7bf858b4392e4a0150d5a8ee794831ee340d8646cf079c07e87c7fd324fd00d6ae94e44786d7da2274922839b4ab09085562337d376732ff7f982717e37ef1cf1704c109219ca64ec52d913eea09960fd2577d9d1c0bf3437ae7acb907b3b3be03a2ebb76a38160c60f314e32413d2d28a0fa7f7b5b924c419270a8ea0af35fc4417c7661f2567ae21dd0be80ac9d53d571ce79dee6b3dcdbdd090be2871ffa1c4ae18eebe1c4cb57dede8030739f217c32ddaae3c21bf13a67c53846e4e23c881078dd294400ae5549a3987fae2180af97e05c8a078bf1356944f7ee3b23aa475ef570cffba2e14031296cbc9d652ab4793f445998bc63018ddbc75eccb10d88144ca5a038ed01c72d85f84730eb9795d2b317edc4bb064f18bca885fbca8ee1976baa9067581069a310562b0059d2a8b82f059fa9dd0313740115104d7baa6b72eb022e7f7b03c8ed624a26871204a64cc03198f4d08d29784396a8514173064b62fbffbb58526c0f42f72e8f50fb05da7ad15787f5641279fe038cc2d25b9358194ef867f4412d1c2a0577acf9e582126f80c11d226bfe10355fce3f3bb049579ec6a9d65c307df5e537abf71fb7c05ce7221fe637d1b456529c99c9f2d4d68933263505a1078b22aa019a39a097c779895c0e46a49a02b50e29e0cbe00b5ae5af0a62d35064f62c0d49454b476b94923c271bad56d3da52dbc63694359c9c086f038b5f9d64fc79b44b62ef8259ab8581acf5fa8533d0e88e83027b8c341d31cb98be58dc38fe1f55256195579d0156b1cd7fc03808532e40251caec6242eb89b0fd61d7db01f6ea2e75627dfa79530ea0b952dd11a56aa06ee8b34042c9c8f42a52ba6b21d70eb9d06f2ea7a2693a8b2522c5b39078877c24ba0057fb00dece4e15f8659d1603cde9df6b8f25e02b758a3145869bb43c4c419b715d58d6b3b6d961d6bd3c5f980ee131892a42e32b8e98bf7425c6b88fd63048b3d9ecaaf176720b2aa150b6b5a852d8e1b468bd9d5faa9cb9c72bf2d4da74a2cd9f6df2b6e4cc2d884ea3f099bc9053529d4140612664568305ccfb3023cf51e2ccb90be411db7ca724d494408b4c18e45ce9214edf298f63c38d8db85a593f1291466079f88b26a768ec52bb37dc2952d77b87d81a65105f27e7590a79227ba14c804811f405b626c5cde494415bf480dc0952789a2f05e956183d86b15f935733b7ac430ccc9a901e173feb2db209a7810896609ee0d9561b45b78d69785b247302490e7c6a345cfdd38f678b342fb3c4155b71a7f63943e31a844423c38894aee93429df098365313ec5c8f212593b3bf98e63a95956a267e2cb0470ba25c4c869b003827a9808ac8c1061022f5d2eb02ea9cf71081360254e018f38efefd923ebf1408682841a14d54ad6f2b8944149bdbcf99302c75ee6ba3e181acf334f347210dce3765dbcabc901487d1d3e2f50727276f7c644637c45632ba25ef047f13206c2887ece510cd6d4cc0e387ad6c1439e5bd95b793b7f336203231767f176bf3dca3df564d4e4fddb4c2f48657acb88bb6c2728c70f5a8908f79ec11ddfcaae8edd225393904aa2707892e2fd79e73168ec3cd4a5ac4a5cbc64eb075b84d6eb665c0c00761fb6bb028b1e1744750fe69a418f55476b89f40f1e063da2e9c6b95860c87526be3a648621dfbdcd53f94c395c67ca98cb01402ae03a8197c0eb46babf3a26ffcfb464bdc32fd62e9899015d891ff0f41878bb82bd2d4f5eb2251a2231eb747ebc1dec1c5757f198fd9d9ffd76c33c4e66497d3e0bb54b5ea7c0f48e51de66169c65c8ffc8cb48e523b382593d2a83d0a91dcdf6a846d21a1a7799976c379844da1df0f44f5468e3517dfe100bb3a2338f1c40f16bad8edc0823d396dd9ecea125866b6770f7ea92cc40b2f6095cabd9411fed5d6dd5c71ce729f524c358045f470dc56e1c6049317dfc34a08141eea9ba47aa139f0775081e18f1137e3e153aba4ff56e66f82b610019a69f0cf3fbc1bd50b4af0018462567e3e177b757eb1e8b6b7f26a4bf14ce796fc04f9bb13e2185f11330ae65eda838ad0016327be8f399e6e637f1513651b53cd13b135a8da34c5f6edbe1c1506745031d8bd31782960694ebb2566cbbbbe2cff520a47d6f2d5cd61450f5684ca0c5a620079737f8447585aae7b4b9388d4d399b5b391ce47764d35624d223df3578049a7eae36e2489885c82b8af8604c882edf91e59990f7d61b1c25da05dc338805cead39875cbad2a0ccbcf575abbc6e12a9cbe8947edb62461a6b6089afbb9084cf8c72f471891990244d66d14675158320dbdda39ef2b77608cecb808edf5ca9d51e5063d117088a46c4fea5c68464e0e40a8a391b984e93a68f6377461e3055d87e167d0a48c8fe9d9237a8c89ca318c8f7fb04847775a8ae443ede49167000216a9c5a00124fe45afec232dbcc68b2b80e380bb98e9ba6687821c963246fc9f86e539e86e020cb99b6a04b2418a26a753e9d18637288376abe88f90b5a170136eda5f7267525508f1f92ff6128390e426be2623254f2c8f7b096ef26e2619b9569bba5b2ed84ac7b16506bf416be2b0a249fff415a7cb1c7bf69915455f2abb0994443d72bd1bf5d959284bec8d0b556417fb3229c82fa5b720ef6fb623d7839823f959c2b54a346ac689be5c0d843be50d6b2d9d7a5e99cf89b4a37003ccc4f61085307829cc413abda27d88f25b64b445c175cf516291e7cc7e4e359dd4c602dd4023ffeb1445c7f7979187e95adc0a6aa107829b0ae80356c5de0310ecf168c3b8c4bd38dbf954deaef09751e67f20bc2c2c47a9f9826de02950274dcf4fbbb056f67d4aab1c9111c26ac20aca9f547448b30f5b7b6d7437bed89a381b64265fc79d3b49df37f2aa2ff0c03d4067ec57fac285270b0cc97724588866165a3b798452469a4d2a757821d9c9111bbefed48d8670d3828cba6ac3a316f5c02ec33f807a465b52724704df0a3417b68f3bbd95840cecfbcd020234dad16bbe51db2fce9e873e1f1fd7b8df3eb010fd537c4e5da36c884810ce6913093d5890b8f090396754c14d8f3115fa8d37a1422395c486ab41a97870eaf131a974bc1e30381a57a4c7dd3b436a84b7585148a105f45ec283907710ec7f1a1e7c440a7899d554245497d7cea4bb63d83e2b2b4120a923c891fa859d9ff4c23b1f6ed2aa7b55c1e929aa25801af3905334f1b81e1390daae8ad6aa779c8950ff264ee13495ac1d6ab8d2bf3a6274dc6323c9b4499a041b84e1cbbe492f28cc849b56f4faf8859c21741064555a50aead87b6be346ad2cd4b4c37ab19b1ac20d06260efecce9098a28d3c14094c46b45684c975e0964350b75aac5605cab1fde3e0e3a7f76b7e1ac5b445610e4bd3d3e69745c7db6bbee49f19d45da89f2ba08737f860fbceb8baea5f83a45aeb6ca738b14ed148da649b895e8168a11cb28ad4f27036e44878753410791906c91e8f41551fda17a336c9e2cf1a07a97b95f1434147f9fceabc6a0cb0a4ff6578dc21323681fcc614f5b02ca015b43b5ae31a76e69b1ca4e014ed5581f66e782d44b2028dda9405b2c0daf035623b8ebdeb327eb544a88ef6c19fa05c868ce28be51b321d9da083aeab616378bb70d9fdad94ea91baa5fc5360b0121a7b032443ab54a6568a60ddb86f9ebda525cb252c1d7f09fb2ebd15b8903601b73e61e21e57bd9680662437f18d13df908753b879ed50ba7e8533f95da23cf633729f1e8668dbb851a4949276853cbf477a3a6da36e6f1c62766713c455e34bd00d1b7f5d943deddd64b68df479afe2ca798cab184d01d7ee38f4ea763fdecbd41f2d34295e1d0248d8014db75656197929e6be92799f80711ec3e19762061793454257c747a14d1dc5da362614c0e075d1b7452a1aaf60c9014bd71ed73c94519b2d5c1ad7b960ba08e86610326e8f8609e2c202df242e09ddaddeb3c441dbb5d1110738c22065d76753ecb1f967647d3de718d748e58cd2b2d22598f0bdc8dbc91a10c93d041a822ab3ea0962b5d9ad018cd3a7b2c325ebade35b0c151e34792e8a8d00dd9e1a9b5cd9b65e2f1e673cd4487426deb02f6eea0976fae071fc023220f1a4b71003d7fd7dbc87f4f834556cdb858d30d4419b3bd97a0b155887b4ce74486fabbb6506bfa267e393b73d736f9988791a479d16fc344fb4fdc3cf20f2cafa02f97c1a59b1df27129c276a22bb0b43c6afc8890bde822cf663984016053919c1055c7d814bd874ebbdc80af6d6054c99045826de2b340856c4fc0dc0404097ed77228ef4303e4bdbdb7ee037455d73ba3af15ab883f60cf2c91e06ff62a2e3ba329743ec206fdbfd6084500efd5cf47f3a105066fbad77852d2e19bec81cf126c420d41c9c186c0b6b1175b8ee0cd80eaeb53c56ca8398aeba7b37d800ea6958c4d4e0a99ac40a91cfd8e8f8fa5978e87debbfc5601db3eb2ea38c4c8e022dc171ddd18df32d5c394fe4219446db06c2f4f5049a123883ac63857807ef1065027d068494e5ca129109a77cc18418c569a75c4937902b7f37e1c913f04e55135fc3a9eb51c3a3f04765be00911238377c26a734dc4638d1fbbbec0a680fad3506ab57367bc361b783798def6957d692756c4432ea8d3fcb55832f096b26c8a445b691bce35090c3cdb0bca89c5e31eacee1c9fe38aabfa7b4e3aec2ecfa02d2522a0e2bcb4454e94c4476fd262cccf33c641a3b3fbdd95600108ac64abee12e27bb45b5490dde612a63a52ab6a5c745214e0771f5d6034658abc6d7d5429df3c3ec705a4e78c48ebee1ca1526e200331990620fa56d3bfa6b4937740626be05bdc060f1e8a88a53e09dc03fe3a0baeb75d8f451918466d739c87e447811d0e46cb546c73375fbbd724cbc57a72242de81c3de3cff40f7c43d224f5748d9a000b19895ae234e681ad5c012e2371a48a2db994435a51c030fda96759e8e07553676017ce2bd8c61b9f737d5e8e1862f86289f4bd3e6784a566abd5010d8c7cb91a714fad3df08251a5994c694b94c9a30fe72d57dce48154411f4842ea6a32bbe30e269fac8ba63590d395daecb7c6f3c3465cd699c5424672bc6f156d34f08054e64d8d3150257c97c97dc2841494533e5d1a9efe2c03bbd139919515b2e80dc532c338ec5a5443211aed0afefca21e2aabe637f89a1b6be212cf91861102ab6c97a8970d658d3c68a608ec5d922f524dcf7108c9e9ab110ae57fb9e404b752c9981057175ccbc94c3b260e09795df97f45ecf16525eb324be547c002da88025544a3e477a4485dec6bce3cc2a50995a070f6c6e3c9c3d741a365e8b1fc2771cf9641e65f456a23936ea54a0455b1c7f088546ea9fddbcf70349b63c99875e1d2c2480703fba7d4a88055fd00c1fc987b760d68966012f650c7744668fde9859ff16afb58c1fa50a717c7b25e0c81939564182abbad99592fd2f063ceec49f4606051d0e0ae02e5f2f5de8adc976c372055fe06c79d9ec3d8f50e76da21adc9c68359139f1315070a3ed60ee7433354a253700be3526ccdacf5bccee4b51c74d80883d49adc7079fdac64b9a76af0667c868557f9dd9115b852309ce9b70a11ef73363743703fdc67d674656d313af2a0076514f950db610737cc97dea3aab314626efaeb220ad92c069da52b159513f1277c90e8f86ab12d438358c529bbe2a9a3ab5dd8150ac2baf91c5e9c602e9cf482b340ac1d5ba332894ae83930fbb9fd9a801bc86b07697f041e2bbe6b5c97e20ebf45b78c407c50d41d999099689d8461d591fe661a03519fe6b60e717e16a9983fa48ed3fc450f84efb871180ebe149122726777257d3dfdd0dc2017edf7b90cf66c779e6a462526f302965f5db9ee65c367c1d9d9b1a86021b0c25902de2b046e036c9e4db78ce061dc6c38c3ff220570709bfcb2955cfa5274c9c6fb206643829f49939a36c3a5e3dbfd9765124513a06a6e1bb8a7c6f798aa07c16415e02f4d8f95ac5aa81fc7c06fa1fac05451f13d4b9c1d7d533e8ab66a7dd4b2cab32233258e21021087c28424c702a6d5c1509d17455e2e0ba14ae4e9ae60ea5042c63709dd0c684445aec11c16405afec6362e1268e4b455ffe28e1a7b40a6eb8020ccecd5521d55935d2f5f7bca1dd04e1e8a731a3eb56454deebcb0ac075955128b02b0a747184394a797d4a02cf2a78d400cf128c7bd3283946ca12e54d9c0d943433ded98ac057154fdfebbb313c7f90c001e2a2077a08d1c49269c84f9763ab9d0e2fd0fbce8ab68a43f77bfdf3bf2a8819e433165f54c7405658f3407c11432df9c86dc086e6dd5db64811b88f900e31fd7c7f3c654e36d4565ebbccdd8c7d6f5364df71d798d6718f671821dbd63dd48c26859c2c349f42db9b0581c62e8fdfea184b8aaa0794665473d3f041a638b21ac0dd95098df949bbfb489355606d037cb77b768d36e828aa6b34d636dcbf67035af7321137865091c56fd6f737dd276b14d6aec7d13697683d6f2b6a1d74f08319f22703f4ae1c3a650af010f8d3cf59ac2155d9782a1587e090d843fd5cb6384c33f13c4fa2aea04ebe5bcf3643ef0f55ec08d7307e127917a4b48e794de9d392b443803aa5fda816ca479c222afe049685059ce01f529bf2e367adc16a2670e3c982faebb83a46f87195a690cef8f92450b8765570187638cddffa240dc4726b405e83a937077e4def24b1203338caa66aaabb3f101017d0ea4364ddf6b86199ab14c20718cad1c72fc269a411b351572b62ac4aa5d65c70f1f14da8788b62273bef17ac4be3b5ac21625ce551f8b832364093d3164445c51651de1072aa10adda254241ad4a2b054f17ed476e6106e4e057a70d9b8eb16c80313d6e1e0b0e22be04d6fe74cb7d43b45b91245350196282b34eb24676409d47143a3a4ed8d4750ee7d1d5a9b995e4372eecaf1eb119c73cb30bc607b2c55ae9446938b609bfea2f2f6af701c4700b23d72e9a4ef733d5b099b68a003a18af42b223c7d13d2d3a8971b5a2928c2e1ec2cc9f39a2ee94b9dcd5cfc9b0a7e914f0693f24e1c84a6fde49d844a4aabffa08025a69987ca1add9c867ca66ea1230f0b26b281b9a8583a9787b915ef346b4feb61bf80cc3314dbc835e0702c1af220446ee39f8ff0bd13ac06c8fb19bd1f5e7c3c892e9f1e4b3759e03f288191b3177f2c1885cce884888d6f8c41d7a3a28bc8ca0b474972268a1c076686214689b326b8116c978dfe0d02dc9e1645a3f1f37676beff8c3759ab02bf7b8a8343a493acaf49f9738e6977c9697ba665db68019c35ca078c0fb0497901ea9ac29615010443a965623fa707d8d8fa2772459fcd9428145b39fbe26f433d8dfffcbbaf4b72cfe8cbd283e5bdd2dccdcfd3308a856436072062da0c417728a95c7f1225dde38ac003412e25c656cf22ff389f44e2b5bda0ab2eccfb9abeaef9776182d28ce4c3030dc1bb748950371d118a1bfb4eff0b03e2a4343205d4e13e75b181868d888f18c307b46734c399a136a1528966add6354768b13f765e2db31dc744fd4d04e6e5cc093f1f8f229c57aa68d855fcb003e7792e0cffe3dfa70b6e51505b98054b1b6685726346ba6c5a180996658bc5b60cd6b70211f4e083561bc70e6c341edd14b500f9703fd04cb5959e82bf23f176adaa5a002f744a6530f8084a7c1193006f6cca025fed7ecf3bf9a876da14ced4dce75cc4401d4169a23995002be2786d9aa75144d0601c33f0782cecf023af0b50e2b50e18243807f8cac04bf547e722939243da4a4eb23d987a9ab62ff996b32f0f87456a6c2e954a3d1437bc76fa9be783d5fe3f19dbbd36a9a43eeff0cf59990e5e5c50f94ddfa38f76bc56c20342c059ba0bb3da22ca0c1df82cfd1288b76fb0bb3c244629457bf1a13c88d6aa0b5614d7f040468bf07290de66dced2b321eeed3d6ce860c906215e1ad082647945c65e08c810bbd231a82e6ce81a6840257430055368a830d47fbfd0eeffcf42ef077346468364b80984cce62e9d28e159bec473e374ae4e81b408977283bd5043492ae5e873347ac83a70194ca8ebb8103eb00a3cea3dbb9992666d0969f56ac0e85fda0e2b82ffa1a356d00b943eb5e6c5930d124384103e902963cfff3697606429c1e952fab3718b0f7a3a2a1d98941cd70eef552c8a7989d8277beda45dd8624f122219116459fb131c43578067db61e3095b30af0065f1b0e378aefddfe0ee4a79d468129de22a02942541ab5bd0aa1da1f0e7273b529d308d7aacdd9f1eb57bdb6058e0d318ab911ed776783c539c62139a35518f7d4876bd52e70d5274cf15c548d49c005075cd9ed9a40465b1339c2e858e2417120346af834fdd30b22fdd0384c60dc3da8c6f24e2f83ee619b6b9e8c303485b5f9d3c8fbd99b0a4cd76fabf353efd508a505fc072d425328c9c7d8d60415c3bf4302c355e1990b8d9b0a25b89188002e1441a3c126ba0a2762201496f42ba0d8f806f636a3cf8dcb65ead002cc4ba27296b5e8f21088b73efde6b5c87e21aa0fdc48e285c574f78198c01d23d37c1f275a87e60184e0120711240cf1cdc745a7c7c545aab0f29b0988363f376064974ee836b369be607335c280f90588e8c47500c77020ef97a5b3831b18868630eadd1aa8cd22a0317f3a9a23fce794c775b789a1154e905a63e15505c0a6668830adb8da64fc31100fe57dda202b2f479713b666f30b09e25be16230b2edc0a01611869ed1dfa8e5ae739dbbfa840201e48285353575561d4d0d07c4cd37f6734fbd0a3612c7c2c779aabfbf453ec6eb503a2020ede754f3f6b821986babf407fd763a052b333a7b7a85a9568922dff611718c581efc140d82e38761f04cc59998dc6796732cfa26c26f13872c4143efdf57d330f26766878de95fc54672e10ad6aa0bcff7332d8ac9cde5bf51262f0a277a81f279da75c31de55e5e7117a5ffcbcf464ecc4f0cdb2eb8732dee34e0e58d9179856f351864425bc56abb2bd017710356567608cf1881eed81d684339806d9cd9bb1cf3f77c748b61deb466e71667f1794dd59a96ab7934212e5f38adafaab7cc2066c7f53949d31fb689fbf468f8142b305153b5a4822b6aa25988fb46f8e2257742c89ebd1300b9f259c416e3fc4f5cd62861d339ccf9d62e3731d434d1f3d1829286271caf022626f9321fa364e3c686619e21c0fe903dd51c103136773db16a5f08a13d42b505186015d55324708a920355bb172086ead99012bb34d3eb7c7965fc709e7a446ceb1170579b4de906f55edaba5ebf5a80ffce5cd95a6e70efac567e920f28a63703cd55c4caac1ae2f13d4452783c1a84bcb8671361b9815c56049796b660ef999cf7d3640db4ecdd12e453c14d63921c28a9216acd644052ff17ae91875823c2b227e80a638db80cd0cd130d4fc3f1d1da57c8ca96b6fa7b1c54942f733c75764062b9d20816ce7219da8edab8bd8f2cb0127a185f1eb44f8479cb1f15223d5561d0837b9cc7d99f9937b56b88b14d6e3bfda46d32ae99d06eb4309a9b70fd9f13aaa16d7bc98e9218373a46278385bf7d665b373a89c4e0b687bbe310008325fd2de0d452d56c2cd11e307b6b467f03100f647a11c90a345a2130ba3198466abd18c926ac93ce4f4331396d40679ffdbe590695047ac3eae65febb797a0b8b16e98ef77304eed8749a75a4dd0e5b9d0e362f0e4c9cd55592c32735717715ce784e59f8f49ff5f79f9333a175bdcca0367df1f6ae77a73d227ee2f6a21d0c4dda9600347801ac178b05e13b8e6c5b663a1f38e121b77f8dd832c5fffedd6ff1a2a15ec1b9f1089bf4ebe52687c63a4c5c98b8d68a9dd8f3ef40ea1915aaaed2c278ec1ea70e8859e16d3ed71c89992b195d0f8e03ee6fd22f3fc16008f01b351afee8dad1e978504d6f7af4e12f485f7f5615ebed49889f7bfb2eb79a3e5ebb1fe85f2806e06b81767bf580aaccd87758e3b1e9a2f7de9db16c3daa7d58849bb682028de6cf1e2bcc1e53e44ce643b4ea0d478f38937d3001189d1f2e618c0b0ecc64ab903ddfaee4e10ffaf6bb6fedce1626faff7ff949aa12298af633504f0d1f94995831debe23df86819ca0864a7f6799f1cf0d51b2b39e79395e8d40b9d46e87bb07072a74805e4cc9432c4f3c500ee974e55d12cd592d56f68d80e10f79a46ceedc6f6fa7e5a5766f776d89332cf1a3b7c91ec8d9cd6b39b4503b26b4414772f035a3be6ecc25015bfb8395fb80123e132a9f180b2d88d5ced3f5d04632ad1cf57a142e34165bce885df93b631ea11ed92c500d29b3242cd0b1ec9cb70272d7eb0bc8788ac5c4f792cf40c509be8cf25724e3159419f69e2060d933c1e7828e2c103facf1b43b9e6c157a2dd4c1d18263d590bdea1574b2e15beea6eb10885b12e3816a709ab535951b14c0db52fb8351e512c036c036ed8844f8f71dc49e7102179b012a8c7e0bf1f56a0079eeed1e5871e086e0c9f2f12ec6876473f65f43bdd04e569be3ebb8855afb5bb60cd928b072d0021ad2718b1d0dc1f29ce5527f19eec225a4597ae1874f266a500d3750a483c15de179802eba6f8c6901a80426284cee91b60ddb7bb6a5745820368c8e577a05d69addb59a51d913cd99c867520920ed1b5a3d12888751eb5b59236a713077fd1c2f962155891dd29b4bf1f83cd1d1611e7128fc1ebb9b481aa81b1c657ccda5d8ef4fccbf451919df50ce0e826d51a762e0e0f68eeeb977f43ebff504383c3182294ae4ec318d59ef928cbebe336258e99f451c9277d4634abedf39b966e7715225b1d20a9193b71530410f91d5f340a6c8293ebdb8ead02c72baba09002853f9c41c2a786a0aa3015a2fc0f30ffa5a9ff0027d27bb446870d536e5b60758145e7c700634088689bfe12ebf85494fc85703b267c5a4c9ba785881f88709cd320eb53c391fbab8b567e4d5594203d2e303ac46e2f65c7fa23e3c612ddd0d89aaccc1d2581136d17df145b56c9f5bb988639c365e974ada2d36efb1ced2fd8f75b8acb06ecbaa51afea405c97b53aca1e22020b81ba3c06c932f1750b6d02e1966b1c4ed8c705dc003ddfc02149dcfc0a1db0abee4c684b920e8ede916f6c4e14b94765917084944063458e1bf8670894e2e905c121dc4f86282bfde5bc3e825383b09b7d2e2ce29603bd6f94707b8a54e726e7ada08d25ec956931d37c17bf36f506829d0d1a7f46884ac0c6b7220b4df5289f0a610b42f545f819e650d9b0886f808e2d3f683de8d0784a6f6f74c0ae5b349ffee47b70e92ac00543f7e14a27f009e45b0b8bbb9063a785da4d96a3c82b5e3235f06dee9103680e506dfe077c87d15031b6f0033f163116a03592ca8ee6ca430595175f5768b2426ae052741f7195fa0b8e9f7ee4a2783729d6841ca3a2b16f13bb2877a9429d6798a90bd5710dfdbd68c06a81c35e8fcf2334c929e9081e7ffb10c4cfe288ed8a3a0fa526f47a2abdc236d6e5f5f18c3d27e7dc3d10f7c0a0748e5c7e7fee78ae9df26cd951346282fb12e03c82805a38ad36e54f5ce039aee13ab42d65c5de207f46143c58cf6c01e1509c911dcb88709d03ef3c875482ca66f3a8e5316f0426dbb84392a1030bd905a2e1cbe871e4894e91af598e0ddc739a28d32891760667d6cd020f08b99151eaba1e161ba65088cfc289fb348cec7ea8d2646c9b61af15dac65dbd45c20a3f555ca2b74a3e8bee80b1c6818078e3bf040b3fed0afa5c587d39e41053b571d1bdeba0b492b75645a03a9631cff151e82996b4e2fb9e067dbe85db9a4dc0b93060bbea56fedf3a88216aa5616e1deb1f12f67994fc6aa8edfbe54fc8543428cf93fc12a447780feaf65a191742df5f0dc1d0cbfd93e1bb6c2c429b47c059ba6e16b89ce8668a153f233eaf43dd83d8fa988f1faa44e13185a55dce354a765dfe8aa3f423ede1ee3915491397aea9b17dbbca3f5055b1677bde37840036ca9698c1ef47e8a881f0bbcb8d6a3570f27f54ad346d5de76aca48f1f342dc3a9e42d59a5d71f1e71994c5e26c834aa707632bd8b969ec6a9f07f8d48e5116cf030ce7860dcdd2c870c375285ab7abd14ddf075384d3c2f7b3b2ebdde93bf44c3e8d0ea32ae675fcdd9af3bbe5b2a5d2b99ec764a0d9d1850e80a602ac9a6bfb22310e688e102bc5073f4fca7b23ad3099a4c93d0873d346ed27cefa0c73fe86bc166fb766f0c8d63ae49aef94a4201be0c64dbc2ccb9ed7159196a473336d564022eeb8f1944b75fdcb9acef8eb6c8934ed2d9284763192357a21b35a795d1435b09a65b5cf4605ff3b10d6823119f7e102d52618ab68c91abc6b369b8590fa391d94e32b36c8b10ae3950b1e42fe816a3137d1bddc2234f98b722cc299260cca1cfe45842a088cc5a166a8c6e35d759d3c16132ba9c113c175e86bff6ebc3ab008467499e7f86b9ae0525cb58124b1bc8cec3941ebb964777e2f12de0093c24e976118f9ed7545523ca1bdceb49f9643ef1f6ffac685ab8452c9e4c7a92aa8b2138bc25f2289921e576b08d292b176fd19219b265a372c343d819a9940fe00963dacd8182a71b2d126580a3fa885879af039d71f14fb45c6fc5aeb5faee97090cb6ea4db8ffea9dfc5d08acd0d615e84e80c73b52a5cb6a17c59cc8a2edd19f5fcb14aad0934b096f7517f91b74a393ddd6361760580008875d6c479f52667322617db6e8bc0c9c98211450f24cd84078993372ede0458bb2f510122c6465d7ffc915acccc5e6d9a761e68cf6b1f5c8332b992dea837675b535ffccd9591ce0f9cef56f9d224ea50f164de3a85046b116b3bbf69fc11a42c32c347e70505250733675ee8a031c10921b1f12b7d18a710b85409098ea6afdcd68f46a242cedc6d04c80a6b41ec9de3c56f39448b8597b22098568866dc9a1cbff0bec5a3aac4b3049b1b4d2c8b308dc534434a694459d79083ea43195cfa04da04dcf982d0e02d8afa6f4361415994aed7456f8ae22534c541dd524fb68dc61e22c5d5f98cb39b3d716095d0c5f6b3c1e856625c18d060ba00fbd0895b4d5e652c2d8fd84d18f075db456a7c59f8d883cbe8b3fad0e85e80adbbd855742d816c151d375e370624a5e8ced5cbbd582416b59baa1c72092db0a34a4fb540bbd1b321b013a1b667a6112d75266a3afca634b9756844379a19b568d86390fcc898208a43b1c187b0a91fccde7aa1559f478f556270accbd299f662eb72ea7e6f2a22ca6b4da2c19da995e5c242c631e68d333ac288ac78783c686dc4f6b8f45af37c76c8f1a3b566f675239725ebee51df464c83bc466d52e5d55c1b49c261fe58f2a826a1ac581df476588bc3f25cc6b4b1798160c428ecc7820f4203447105074a8d53d391e43899e61bd682eff4895c1ef53db0211c1d4a63abe026672d2c0be157914981604203ad8102df6a81bb73a0f54491ceb442bf3be8acd8b02480cb67c6b874f2ea131a9b5a5afc867b3404c94e955477e0f0ad11527aa8d2093b8d4f3dcba1d8b7c7597e36d713b6f9452d5908bf7d67ef8080dfdba6a8672ad5d76fdf8e945890e47225e7ce7397ad15eb3d0c6e1d3a44136a8a8007892d389449e8166d7394f76fe25873b9f176dc7325be9d404cf204815787398eb58d36dc378222553eedaa8c175be276dee77170778ef973dd46c20be315b30dcead1d8ebb8f7fba4adc7be6dcd8b7080e11b1cf062a9b77acd1d5c65cb657c991daf261018054fad8d0feb731814b98292ab77c5af4a68f1c21656b0d4843564efb8b9b2a6f8c14087f61a90e519016b8a02d569006a7b2d6af6f2c5561fa2db1684c4fb835f2b64b3aa50812c8cf2a3d2f61322f318eac3d0069ec3b6e1bc4b42958b3069329b2e1633d74f581db830fb4def553e37b6a1eacb64d02e5906a543155f69b1d13cc064f77ab90caa9597816ac021bd26735b0a402780ca47427b19910aa357e79c253b05371942e67ea0400b8a2d5a92514b1940138b2f992ed5932378d16474923ee8e3e46f489c2a59a793347197d7fa183805814bd077a4d1afd38f02ba6fcf617ce515d5d76d6e05db3b16e9458418c185fcf3eae0a5372291058ea87ade118aac9d3c991feb9adf23ac56460d06ba22b3ac91d10e28cdb77754bb4c60b8fc8321b3d1d84862ab7c8b2ba56be3b1237ad4d9975e0ea611e60311d8916e4ee59fac9ee459a449cbe89b60ddea53f7baa122b123edfb98fd862f5bebf4700bfb0852d92d3eb5b538da5a391f47a54572941a89341f83e20b709b760e7f5709574308e49b63e980a46783185aebd2e770e0e431b2b252dd4596657726ef65fa124c3c9a67ee7ea216f3ce3e455a7ed074f4b4eae7f6c959f106a5e35c4b1ef7faba345d4e6ef221b5aaa3882d6ae45985df01dd5eccc50fa13e0ff0353865bf701af96a580f0d7407ec9a4568796ee5d4bc4b72c020f22b8c9e807caa7ca4717283872925eb19463bbd92155ca27f4d721fb71f8583ab18f3bc0e33702ab725b7cea4fda0cd1c972c15337c87491d8c6b3de7db2703f6913badd903eeb55d6ae1ef1bac4b6a97b48893c66dde47127538bf0bc853c8ac15a121c031e4279aec558b12f568307fff6f228d253754680eca26cee3e77a0ce7cfa192463841d639b52207ca8c12dc8421d1a48ab9bbbf8cc47b39487de13fd9f1e765d541582288244b197f72d08e3cb83d2714d4c4bb6e1267f09c1174a8bccec7457b79662cde528226ab962b4df3fea2ad4916c5f38e95fe3f18bf9676e3b49a01f4f6d29b35436c9161d217fe5880d05e02cb439c8cd93163b17410929b4c7e51c3d510f83cb42f65d463865d66736c1997883b86cb6dea3fdd91711bdae126d2ed258aee3447f42a21503594c486988fd66275bd7c35d236c39c6f874bb5bc45471966ab88225b930ed5624d40c9ecbb53c9cd722154ec60fb6a96d9ab174f939cd2f67c87180c75ca206e0e334a6b4182300502e250c8782d3affc575b18252c3de14b7003f0a29dd70fd0f028be0b62e72ae2b8a49caad755c9f0b59cd8fb1548af5834138012048741a5ab91151dd45b6afb6b35564b410860678ac7e4ea3e2d369488f772b02a4693d4e7516a21968fdfca08aaa5c295c12e2d33929a716f653da32638fe8c895d223e3e382a599899b3232159c63ebb3b88c82c5f6acd4862b71cf167d9f6bafd9d11d235f9765d2b0309c06bfe4552d97d9e41b558a85e8048508793298a34c11105aa23f9b4ea68190c7a9f28c05426a1d294270507f8af7c4f506b42f0e5305550c58fa831f39af1c7e07f9182e4a9f6cee96770a1fd67bb0f614bc3bd7f8d4b917caa494e04859b47f7cdd5b8b08f3c514ce7f0e8a2191af34bb0e4136f7b1698c00612e47dd5416b13611211f670d291aa4e814147ee11b5eb53eeed90de0e3a8b4a6a5e2c9e5b90239c0010f13b7b9e5b97291290c822e4ae55132c5599141680db5e0ea81a6519f7de2455362df54f1fb27719e122cbb8386f2fe02c11e1ac9bdfe0ee3b6b969b048dc29879c2cb574b8dea561ee55b896edbd560ba05765b2c0e2b75311c8773f626102159e1c4377d9332c22f7257142c2b7264d7c1409801c11dbda545be4c8b346ae923c30302382b255334f149e08f10542a1597d0c762b15fa4389593dbd436381a365de07d21dfd34f3c3961b06331c2316e3e43ac1f68844f3221e4d23487e36ed2dc066156d1a48c5e4909ed77fb87827d02949cec4dce623624b500ccc8a55a938ba1d24e84d93f4a5ece269d19617af1189f492f743c5d32add5276b1b4a4252388a54557161b877ae6307a7790a8072de90b11c5d3929e8c24968634a5c8c4d2a64f4a4c1d65d3474b3a3292381a34e58bd941a21e5ad215228aa3255d3292781ad297892c96a6e8c810c5d3a22517758967b43368c58f3aaa178e48e849931b603ad19be614cfc2de84fc48603bef6fd8626444f4d7d4313522ae80433d3b9c5e1d24e8a7254d311631b4a149864c3c6d5a92780ddab2d839d49b433de0504f1cea85c3f4ee48dffa69d09612a3a36cfa6849978c2486067d596c1c568f8e43d04f53da72da6269d197c5dea1de38d48bc3f4ea30c47a68d096c46ad0282f360791f4d3a24d4e4c4759d73b5914ba19280bb77da275572e8dc37a71944ea17a3a5dfcdd6edd29251237f4b46226ff406412adc83b0742598d5f047f6dd7a76c75ae53c785841234e54221cb4d444204ed02ee3545a70d3740b0a6d0013c0c3c0c3c0c3c0c3c8cdfdafdf6b1933099a424436bf85eb84d7e22a594524a2929e543448ecf03ec3bc0cab3ad6dfbb720930d800d920d864812da747ed34f3aa5912944824cf2a4e507296a2f8610492a362e68937d25d366100957eac25d56fd93d70822f9dbb35889798c2e4d2092e37a5a8db229af970610c9316b96c7da783a74e60f49aa5a5da9d289bb4df14382f6ca29ec6dfaac4ce94362f8cea7414db7a694c287c4ec29ab57aac6242b650f49e761fd9359668f7bd143829efbce97e3062fbde421496707cb96bc729c5df09064ab7a29ae5cfe30973b247c8a2cbfd16c22dd6287c4caf461c478c7f659ea90a022164e3c943669153a24d9de5f69ce7db1937c0e891f43dbbfc2d4eaef72488cab709e4e3ce7f9781c9247e86df2f6d2271e874372cbc69c2b4a557bc6df906839746fb3637f4c713724a93999574583dfeede868451d349d6b6cfb3ee6c48d614d5358dd26042f6352455a56df8d62c7d525743c2adc6a4174b6948d698f534cfc958111a9234e88f7bf0d4aeda19123df3bba7ef9e59cacd901c638a6b29bd7761548684d78bf133a9fd51193224a8142a5712fea134650cc999a3efa3eba8c92e86a479131fcbf44c2a0e43d2c76dd9bcf81c832131f5292164329de7ef1792ea5eee3666cff4d40b49417414bbcdace5b92e24a85cd641a6e4295facb990a4a49dc586af9c2b5b48ce964c7ce6cee89eb490a017f4670e0b53399f85041d1bebdb9639e7080b89a149e9852d15c6e52b249808a9d9ba2d5e8e5a213995880595bd623cb30a494ab3728f18b19b412a247dcc9c16cf3b66348584af55f3ba8e216d6329247fbca0ca527ebd8842929e0f11fa848cb974d60b282497caf1747e8adeff592f9e90305ab3e25e855568d60b27247d4e66fb698452abac174d48ca94e2e774d3f1d45d2f9890ece549379b88d7a5ebc51292428b9df8a47e5ae57aa184c4f0f81032054da2e37a9184e491ab9e64c7f1f270bd404282a7d0ff5e2efa4db65e1c2149648a371d69af195b2f8c90d49a4cb9074d79a6a45e1421c172c9309a455e8c1df58208897239c53efd58c13aeac51092aae26a3d2cfef4a25e0821f984c852ef9ebfff562f8290a0da3a6d8ea7a51f562f80905c2958766892416955bdf8418299f5aefb89c89eea850f92f532fc37c33c2851bde841d2a5d825946a0a6d39f58207c9a75d3e53cca7db3cdd459230fbf5b68e99ce94ba481abb109b637c301d94b948f237317daadb2de5242e1283c989921fd7e33d798be4b3cd1b2eb788ac3d6d91309e2d7969065149ce5a247ccc9f2a464a8b2435a1cfea368fd6b348d46456ddef622d62ca2241878d6b32bdccef271649fac646e6ee0cbb0b2c1253eb07fbdb9845e4572499c85cf1747692bb2b9243e87dce662bbac25624685b0f4a33e8ec1e5624dd9ce6153395765e455265d61c9b62cce715aa22312e737ed764e1f42815899b34689b28612a3ca848ce61fbc7f63764674e9120f2b9def3fcfab6a648eaa0da5dea63ef52578aa4a4b3899fdcb01b46a448ca9215948edddfed2892e7d3e6344a45ec3ba2488ac1cd2cde65be51094582eaed4b6a1eded60645727ffc4b25ae64bcf613c99683b85ab398c2af9e48141dafd52d7fd07f2792e453b455cdc70aa371227953ec532731154d9b48da198da9e663564b9a48b01cd69a4698f65126923785eddba0419a8d3091e0712747ef2695522e917c7afaac53961bdb1209423c6ee4e64e31772a919482ed6ea8f9aaaca144f286523289cc28f2f449248e98868ce74eaa6f4924a83cb5b733d34a8944b2d5654c13265453098984554b624a9dd0b9ad7a44d2f997db753a55b3f4ff283218a38719222261a040248486232c8f16f42ee8d6773b9236c256ffcdb14697e76b84cd053c762023ecb9d1c12fffe6300f30682c623b77abaed75c19338b314fa3e497f6d4d8df052ee00186193cc2080971018f1dd8e387193414917cafa182aae75bc48c40231109aa54dcc4f96ccaa16711ab010d4424880eda423c368748d0b9c7e372b00d13dd0863e002340c91281b72359fb9ab675c88241999df71f74bd9bc84a8f245c6e2bbda54b42dc3d2e82b31273ffad14124e667668e9994365349da4ba021880491ab4c63b9316c530e030522233402919caed42ac547bb6c392d724aa00188a46bbd19d9b0735f4910313200c22ef28724fd294d9d7f4a59937e844dfdfc906c294de5f5d89fbfdd089b0f91911f63cce02e0434fa906469b3c5676af7fc7184ad7ddcf121adf918abd1cdf7d45bf1f0dc9bc4657be7239ccc80c61e9254a7f3cb762a442c0e0909095932fe878f1a9c1368e82131374cde52e49e6a350f89614548157da3ae6efb020d3c24c616b1db79b9fdacd6ef90dc95cf4b8c667de51d12e2638719648ca17648b6b8254aecff9e05a18e028d3a24c611d96137a6b0416d5c0a34e8907c357aeb29a84d533935281f080909096911eb3924c9b6d3b9d87ccbcb8eb0892c1815b08007c0c8c0fea081c89a1c1274364b6a74de5d53210b80e800a9008818608094314619d11112b255041a7148fc304a36c3c79853a547d8447af010f9f12e486b86831ae35eda6f0e3f2a1cd078439515dbfa5e31df25ae6e33fa9be766d3fd3fc286811080f0d1704382f818a996e136e5188684849489d8f1310619b9db86245572e1d54b789c938fb0f9069c0d49b59ec1b2b767d38dd6909cbbe632938e3a731f1a6a48ca97eaadc28e16e59e3424e8dd8c4eb1f9e9b28e86c43a53b5ffec52d74b0cc634d03843f26ca59538355aa61e3324a8536b5531e597939a322456d069e999f5494b4d86241526bd3426995296ea0434c69098d9b4c7ed9cd2085f0c8922f44d7f0a72d35b320c77e7578899d88deabcee9ad2e9fbcba36e9e0334c090a42d778678cb3eeea62f24664aaf54faa1df326d068f1e1708093183470f54011e3fcad84048c879196c878102911f34bc9074252353d9e9a7d37721694de5a0acb389076df1d84048480f1e3f0a0640748098513a10126246212324c4470909e9f13fc608020d2e247be5bef24e2a27151bdd4282d052d6e9b334e690972581861612ee94fa97b71c4f2352cd42823c71d2fcb54f47223c7e943106f2f851c60642423c0c1488d0f0010d2c24c7cb318d0519eaf4e46924681d34ae901c46f543869568524148cf001103c4e819201b060a44100d2b24beaf56ce0f5ba3b6211e23ad6ca05185044d8b415b5727f51afa5146f9e1a30c1e3a560d34a89020ef640c32368d1693d6a0c78f329290904c3311cb2924c651315dd6a4e6fd63c630a3026766a02185a4b427d308fb33ed8bab462139c6e4969f94b464d2129226839010114b93014221d12a69dfc88aedd66b846d7702349e9018ac2c6495b5a5a8ac193510c345acb140c30909ca62dab8eefc9d2f4b8290102d133116038d2624c7a667d1a0c7cfe2443b7e0c0c8484f8582fd06042625a0da641bfc646116bf02324a44cc46af0e3ae40630949754ae9164b1a2e969f84849c47628001923e0421212185041a4a48503a73dab4be1baeed11363278f4480922460640d087848088010608193c7a602024e4478f9741193c8ea595814612126d54b8b2b86972bd474262d2c14683fc8c6aa22324668d635f76d2d674dc08891b7c3746e8f609358eb039881819d8408f1e18386f860842424242cecba0cb68a081461112cf747e18d9ae97fc4048480f1e66b8f6f861c60a681021316eacfe734b152ba8236c65fc0e111e658888b8a9880806680c21c99457c72a9329df9ae656a02184e4f7f4b6a51ed4a8101a61e391ca08090909090911f1163115690b03052234a840230809f25c4d45fcb42d2f3480909c31bfe48bd67c8b231a3f48dcb456721d7e99d774860f12c4f805d9ff1c94108d460f1273d4acb315fb3cbc321a3ca8b309cbd1e5ed199f11b65c36838c3170033b48bcf8383a2791ab94322dd041d285131995999408fdb3053948bea07c4e874c71f4651c24bce612517ad77c46957b8304f59c777119d3c89c286283e4323df3cc94fdda228ad42029ed5a8f4ad1b8dab119668c80053448cadee4f92c8ca6b47a0c1abc067afc0c7880c1821924efe57785d9dad1f2550649e329c67aeb3159a71d61fbd143c4f58c04292a5dea12697193d008db0cbe0cecf123a7477911f4f8210233f61f891e54ae10fd994476fc510f1e47011fc93ad7569bb72e7334a1323e2dd023c1345fae7431c34ce7889cea0562901c534e61eac2b4cf3265c48c84d331e67edeb80cf57924c814dbe646e46d1c79846dc71825adf5f8e103cb481ab532b961d5f227fff04146e2b5abea094f7b4ac4236c3f7c8cb1a36c77247a8590d7b71cb7273a4d6090302ba36308cd7d73fdef30e38c1f9abc2041fcb36cec7ca8ad09313046921895d6f28bce5a3a2ab23792a03a5f5c34e9416b5a5d90f42aba56a3b6174c982969dec7d37e562efb8a1918f289e1abe328cbfde6f91c613b9478ad7dc2838dd014ac11361f3c509af6f8e1634f529838611b566a43684c72699241efda67a36846d87688a062c6598fffe15df4cbf9155fda474f6584adcfce10f941466a0cb42039fadd6fa81c5c4fa67d2622d2c914751aa3467f73848dc78f32c610c93d43e47df42866a491714619d8011624c59c22aa29a7d1ade408dbfef0310618be82848d69e3d9e6cfb1611d61d31fdfad82c48f953c070feaa679fc1d669c2146a5203967a877310baba5e41136b4e3c748fae39cd1e38708ccfa07190951903032ddea8f593a952f639c202969ca9c25d672eea0ff6146411540e50321213e7a7c6f18281071810992fcc4dbfd6cf5875cd1f64a90ece796436568e79ed147055cc3408188094890fcb1be345b73a9aed48d20f1e542c7326a1261959b18097a549f56befaa0f7196113f111dc88e4f89d1f4f5534fb941189baf29d7b4c27131786611059073516917815b24254d2354fbe0e101d5d831a8a48aa6429ab2352c3c32811096b2959c8399ff91441749c351d6a2022e9e2c88abed9e8ecce219292526a5774523f0006c89e51c31049b6a163070bb2772d5888a45e17fd4ba1fe6ad61a84488c4da993fef58348528f5522aa8277953ec22652461221c347998188088f1f881c060a442c504310892345d7356daae8350a44b2661c5b51ef20938048921379939d73fe90dcaea6e336a8a0828b23ac67462103fd7035fa9098537aa5bee6e9d2f51d6af021c13ad5f6e8eca8221753438d3d24594cd13d6d2ad3e7a11e9284ce787cdea4c37db3821a7940769fe6efb88f67507a88fc271105430d3c24573a5122aab5465b06224606560835ee90bc27f47addd4ed67cc089b61a00c0f8484f880010f1e238da86187e410d79864dcb954f3d42179bc5f4d6446876adb950e09dac7bf77b52d456356a83187e4ee5827432679994dc35a861a724870d1f4d919d4ee97af64a81187a4d518ee52be4a66bc456884a1061c92ccb4732cd753296c2c52831e3f0e538d37249d2cad18ba93cce997339088489b1a6e48ba0c9ee95edbf4d82652a30d49c1ac346b9acb399a27410d3624a73479c1e3ba9bda7f84ed0c0f4148c8ef30e5ac50630dc91534798c1a63eef50f6aa82171f35344a6b68d0c65d9e1437950230d09737a39978c49282f77844d44cdd190e029b57fba3cc2d2ad76353821216e3538ea45136a9c2129655c50e7a7ff55533324ac6fee18a9a5d366531992ac357fadc751c2749a0c4969415aa8b863d6367ea1c618928352ebdc6eb9615b1343c2bfed8a588e19f72e61488a1b2f63f6f594d1743cab50030c49f2cf34e80ccf7e1945a1c617122eec7d9e4b17b47dc60b49a3eb2974c94faa95d7e842921a4b93cdfbf515b3dc420d2e242571c22f681bf53acfc73914d4d842725d87b58a9d84f20c4150430b094ab34b63d65911f58fb0a1953182909090903a911d669031069a7a01851a5948ccba7b4936a75fdeffa3fc38a303cbbfc38ce18232ce483d821a58489cf7904d66f2648dccb150e30ac91f96f9724c3a236c3c7e5ca186158eb7573bedd4dd92d7b74615123d336c665a997aa5a850830ac99a46b3adbf6c3c399a42f2e578c5984c8352595a29240665993cfce52b9de435a29030aefe29e6bc79fc9246d8d6d496070aa3061492c7f2a98ceb367bd99f906c6a2ece5d90b14ba95aa8e184043db2f3dc6854d50c35214157eaec49678e90bd3321f9e454f24ff1ef59da126a2c21417510a52ead689cbd4a4834e92175f5477df44a1292b2e9931f4d08212d659190a454355db4242e9b071d2129548e29beebb2c532bd07358c9034b32aa2947e62808186861a454892f93a8a8e1962dbb421212121661432b806119282c9d71c535891256465c78e8205851a4348f8d9dc103a2e6d2c2384044fd9a4f2c616693a2508899fdc942cf5f5613d4048cce0b599fd4f984c798d1f24c6d27e9125d36f4dade183c494672da5e87319c5abd183e434ddeacff6bef14e50830709af76273d47b37b4ada45626e7ea8bb9c640ea7d74582b4edb6502a952613e522f9773fe6bb90c145626f0e15c385fffc56df2271732c155dcf730e6d6d9124ee62e859529fbfd75a24e92cb5f23e16c79468919cd57e3949d935e1b9592469ea5c8c9f9f7e4f9345d2c7bb8b8ade781ed517b1480eca92a8768bfe2ed717b070663c36b456c7c63dcfe29d0a7a6a2cd3d6f6c52b92a35554bf531ed44cc9154967ca63921bd4b6c5522b9276d6d64d69d225fb37947df07cca45bf8ac4d8e37297e1d226f9ab22d9454591c9f34bcffea948d620bc53ce35ede97e5424f869f5a0bfdac4ad7f8a042527544c63fc6606992249a4ff7f4a4adec51b9522b175841cbd2ef95a2352248baaba4a32d38f9d6814c927aeb4d775a7d29d238aa45839b358a65b2cfb84226936fdd4830e13731e50245c50ba42eea6a0be9d4f247f4c55d7aa97791de389e49cde3c7528a5fe644c27122c5d124b8baf6119c389444b6a5358cc8c65b6d944c26d4c76b917935ca9682241fa85cfbe5b6ae32c9948ce7f3d4b7193f1c78289c464a1febcc57289c4d8f419369512255ab14482f0602964879a93bf5722397a389de49b3471df29919c53f8f93f2143fef64924864e26f4eb9f4c17764924fe475b55bf4bdbd6239118355d53abae2d491d1209eff29d93d0d9c489fb11c9aa563184bb55e9b21d91f0daa362d4e6e449eb4624fef8e5cd72dabd458c48b8ba4fbf39a8c96878114941767dd6b49b960a2b22b1df9350daffd288cd89484aa9399fd94544e2096d5967d14f44e44324290faa4796e5e8866c88e46fef0e6b9ef227954224a851e9c9e2c8b5d00991203dfdd8af8a79d53b88c4a429dd293b571049dafa72ac774fac490361213fbe004482f9e7d7147bdda2f287c4cae041eee96ad4d60f89213a2a538a6ee2be3e2478564e224d5628cd940f099774d8a6f4592a4b7b481c95c5c22a8928cde821d1d407b9fefda74c250f099be25d3d76f0a4261e92346d6e5a8ba9b28f7748cc61734ee3ee9dd24a3b2479c5ca29d46c4c4f1d126b93e5d0396fa858a243b2589cc59061b2f29c4352b8d341baaae8d1a21c924b260b9fb9f5da3b8c43525ca75aff9c319c090ec91e47fdc6f0ad9df90d89316a5ce57ad69edc9060b5661d7f6156d96b4372c894b126efb72dc48684ffe859515f84c8d0d6902cdf3108e53e223aa886c4fecb3c55ca645657236c233fc698c1da8e82e6812fd290a0b5e5fdf264071545684850fb297ff2aefdf670844d0630e8bdc4176748105b53cfa6c42aadc90c891ece736c7446cc83236c328081482abf43248d041cd0f322115f942149be748bd0da9053213224f89fd2f4a46dfe53cc1892325bdc82361d9b63e51be8e101f465ccc0036c2b86c48c29283313e2cd831c86841951adbea7a7dfce1136111ea857f00518923a9ea620eb36879ce50b4977793ca7943cc80fed1863e488b8a0c793a1c7e30b2f24c77fdf07197f7266d385044ddb59aee483ebd6236c9bbee042c29b6911a25af754f0b79064a774123a679cfae946d87694ffe14304063bce306a206264400c3040fcbfd042a29fa6a17384b29060aab3294b3ad532632c24e50d3d57dae29fdc05658ce1a3c719e77b88d8bb0025a93c182400d101c201101d203fcee84048488f473cc2080979c4a38c909090100d94dd231e65a49a7981852fae9018cee56d643fe5edcc089b2309c6302324640c338af08515924b5bf4cda8f7a75370844ded4fc88f909090101d201f080108c807c00001d1510312742024640421213a4242424296043bcea390901e2f62868f0d4010bea842b2fde7601d3d4f6b4eeb842fa8909cfeae54cbcbd3c4ffa38c9741e25bf8620ac9be7929d63cb86efc5827e10b292447ad93cb74aa63b2d2842fa2d06bce9d7bc466da630c113356c1175048ec94f479a9184a684c47d86a6044eed09731829010545cf8e2098956eae5c6af745bd28ff0ef30250c1488d4e00b2724c62925c3e6f8bc274b6c3f3e037e842f9a90749a84bf27a5e6ef23527cc1043f85f7e9bf0c75aa1136913eb7447cb1848433bb7c3a64ffdd98d0064240c4c8004848881933282190c5174a484ae1a99b3c8547d8f60a5f2421615dbc476d59695a93063c2c1012829e063c4c3512ccb5b2b9394531a58db0698f2f8e502c59cae37266eff83dcef0c163c709092183478f1412a2430c304092adc70f336898e20b23dc9a7b7352f11d08e23340c81863c447880c400c9f0152e9e38b227072575a2b9f1623321f23a499d618d3fc780619223b7694d33fcee840191850107c41842495849ac6edd0fd5177ec283aecc24081080d2d7c3184e42eebe017932a79292684a41873e5aa3f9756f3839014776ab3a28aba501b08091f64dbc49faace8f13f8e207c91bdc62e7de9cb43cf641f29f16d59d36e97a59236c48042d035ff42041bae69c94c5fa3221f4050f9263ac2a7d32692c3dfd2e1293b5897c08a12fb84817095274eedcd31f17cf45d2eeb796f8ac4aa6e3c2f2f23359cbf92d9283fb28b9fbb22d92aec68490154368cf6f2d12f5c4b5bca95e0dbed222c1eb623ef5d7f179c559249e79a5880e3a592465f1b799cf894562e72ed5dcb96409d96091247f4ccf3e78a81c7a45b26fd0d48b4d61d453e76520d218f0c215892fca3c9e50b3cb7f6a45822ccb9b17bab44a9d58916c7726ce93e8543fd32a925f3629b135f928675245c2eaa9efe4364a6831a522c15d45fff78dcacb122a922b73be2f953cec4da748b4fd3c1d8359ce29648aa433d9ec58aba746a514097bb16e3997fb27f5932229efacb557fd92d83f8a246563b92fa8daa4e11745728a17e74f2e788cf64391b876974f5859e9987d50245cfe65892a9d32d7ff44f2e6749aae7c479fee3d911c54458b8d0595db7d2712b365ee76324d31274e2488f7ce7946bbd4f7681349215af6bd2ea52c1ea489e4b46d9e82a5b064529489a4d2742272a96207156122399bac186cbef4c7449748ce7b167ff3093b15912592f365ae36d927c476a812c9a92978d0ab49b6578812c9a3af4746674e3a972691b839d488c9b07b2a9244a2effd08d5725d2a14890433296321b765e10389a491163e3a3e9d79e63c2269c3253562f1e388a41c47a5d0c952fedfa71109ba72cd7be69c3aa60f2392d5741cfd222c5896cf22122f8bcef0b1a43ce58e2292d2dc2a7593f825d949447249d39792e6dbf4ed202271ebe4f99d1ae19e378748ce732dbad59bb8c6e02a722be6e902c98f1d67f4a8492406cbde2963df9baec1c340446406a5479d2492dd42f856f8e6d3daeccf88413183c772e0462492c294567bd8def8a14222d9e3fc5c6ce554399a39c3077718281061c08d47b82392d308b5a464d7c62077231891aca5c4b24cd5f46e49cdcccc9b57132f310c14887ce1831b8b48aa34fd21524eb659ce0d4524ce87ea9c73598a293331c04042dc484492d2bfdd993f57147311d1e67dbbfc9e37bf038d2152060ffe1d6803ab891b87484aa54f7b6dd452922943f41a5318a5dd199fc28d422489eb18d735175b1b6b4224588e29bca8698f7262186e0c22514bd9ed5cd093972b186e0822496665da895ed16619126e0422f174b4f8992aaf283515e10620924c09e9fa3972d7465848871b7f48ac38b2c3bbd99ceaed861f126d435d45fdd48d3e24290ba59faa3523f2e643c2654ae1e354fee7f290903cc38d3d246cd0a9cc2de6eb20be1e92cd45def5e58eeded794834ddb28ff2ebdee563e1061e9274e36b29d9f8ad31e5c61d12b536c54d31df5fcca71b7648d0111ff4a5f6102efb8d3a24694a676dff394d47bc4187645f3fdd16b3698be86fcc21294637ff4da9b81a935bb82187e4fa4d97a3d9a72a0699e0461c92e2b733bd6ceeee663d70030ec9a3623ba588f518d32eb8f18644532fbabd1b471fe9060ba91b6d480a6f517a2a54acd53ac2c6a38c3b19dc604382dec6b0b1ab4645ec3ebec71a92aec437e53d91a3bf1d37d490541643db9b5b522a47236c652fb89186040b729a35f6548e9747436297de60aa17a428a572062de6dc432fb63633764e4c6cb24cf91f65bc0cfe861912644a173a99cccd692d6548d0de2a1eac4658ec3819927d43ab654aad6e251c43829e97d0539a1343e275502984078f5b392b3fca057488e479c1861b6148b4a482bc4ea299dc523024e5d41be1a56bfb546a031b08838c314242d66e7c21293ce998e784062bcbe0d1a603a412900f80019252b8e185c4ac2e19d1f69ec73d23dce84282b4191932a5cdd1943424c40737b89094d17eb335a76016e45b484e173dd3e6a0dff2a5b590d429ee593ce9b752169232e7b07a1d4f9799b190184dc3bbc73eeddde08d2b24a9609a7445b950e3252b245df47153a23519ff630361a040240737aa90bcc9b3d2940e167a7c2a248bd5a7dea6d1afb3994292091f59daa96643c5a5909c296ad6ea3a98a9380a0996e1374434504812133ad67b2d7cddf98464dfb9243d846cdcb4cbb8e184a45462dff972fe3ae34d48ca963e871322fea387758309496ee1f36bb89c0d1a5f4292658f796ef625a39d5a068f1d3a4242d8dc5042724aaed982becddd9e9384a4794b377d5b3984bd236c3b761411be31c818038c1f639031c60e91911f663c0a09d930502042831b48484e6b5f3127959fbbb32cb87184a4d48ab33968ba918b8e30046e1821416b8eb3a44966f231152149e5fb3b192fe328b84184c49c73e5f985be5ca62ec18d212408ddbf1ccecc64ce56e48610f04610927b2fcb91bbb10ff1f2061012ae3ccf0999fd8d1f249e90ad147b634a97b270c307899dfa4a78cc3188353b0c101d201a0011030c90901013b1ba2fdce841e296a872b57b17dbd92d708307891fbe34c6115ba73c6917c97af9a46bf6bc96585ab0a18b64bd798d4deadf4b3eb948b69773b18cc9e2e51a04ce57b0818be4b10cebf5bbd0d1432121cbb7486abdfe73ebf1339d648be4e0a67a442d876d10d52231cf694ffae52f56438bc41fbdbd909e365f7b6691f0a12cd7a787cd267259249c5edff69c26cf148e45b2e6799172aa4a45078b44d5bb9849cf888e6d5f91141fa62c0579d5a693ae4818613ba639e7f619612b123c7d8f8e561f21845891f42905319521eea7ae22794fe43d7f906e3b9e2a9234eb57479a0ebb254b45b256d834d7584a7709154939fb88bf5d939ba3a74850cfa9cb72eca04e2c5324a852297c7a9e1dd5294582fe2c76d529c70e1a52247e8e0beb6f3f55b11b45b258f8d1ceacf16f6951246eea373996399ea85428126456345949fd8cca1d28923397f03213ff1309aa9f21f4c375e720e489e4f49fe994dbfbe824d38924b57f669bdcb2bd6e9c48cade9dcbb3079979e44d24b7cc9f07b9b43975ac89c4926e5a152a6c8ab7662241cea9b01ccf5dc32b113d0bd8c044d2bc68d32594d6b9e88db048d98f1e3c7c24dab844e24893d9e2e8e8081b0dc62043e487d9181df81112428331c810f9547c6ce003362c91a02a77eb07136bc2f495483e93bb547943864e4194480af9a1e63ce7073df52412a3c6eba6383a7c63d4cce160431289315a85fc65d339c41f89c49b3bcda46489d70647d844761454d820714cbff98f252b8907b247342aeb59335feab5b7a125ebe962c79ab78647d8be8719223c905a9d2312745072a35d332062640024418d480cba7b21b2465bf765449275109a5f54327de21341484848c82212be46c36890a33ecd2f6266071b8a48f258426b4ea784cd986622a68948500de9a1358df8c998b3324424ad76a8aad764c993e810893ea64777c60ba3333744520e1a7f73545d88a4d2a6e479c54bb671b44188040b527f2ee88c5daf6a126c0c2271365af2d4fd3f1eaf2012365c1233c24a49bddc4620123cc8745663d7e163e686600310893157b4fedd31d31bff43626c487fdf9cfd73aefc909c37f5e867e99e45d3461f9284c724f3dc5a30511b6d2024646df0218d639a51f773f650cecbaa8557daccce78b9bce6142b32ed5edc7c810d3d24ef27a552ac747c5fd9461e12d37fbd893195962a8387c4d22ae629b595e53876c1c61d1233696e97e9895173da21714e6c96b97c52e1cb5e838d3a24e5e82893694a644a5b0d36e890e4a9459e8a37ab790ec9272a564a162d59aa9c12d89043f28f4acd995eef2664285247041b71487cebf551f7525a2c0787c46bdb0d0da6294b27bd2131ac2fba9e0e3a6470dd9098e3a2fca9b8613be9ec196cb42129c5b79ca6fa4a71e466b0c186c43a1191f15b26b3fa6b488c1ff4cb8f9fb40ad590a039feadf47a8f1a4f1a92748cc92b6fd9c8c7cc046ca021494b5e545b6dd18ad11912df63c66648f8ca963c57a7dbebd446199243a7926fc1e246d9fb013040b860830c09da6dfa315a4eba5aad1080e800291080e800a90f80e800290f80e800a93040748054074074801418203a408a03201f0003a4d668e4c1c6189242a88a592aa86c8821b1623e131a553ec60bda0843625c11631d3a795edcb160030c09ea49c79016648a5b54c760e30b89379af3c63bcdb7a48ec0861792325512a76dd9f62e6784ad0b49a3563c7486e67c8a3c0ccae14252f6f7ce252fcb69d0e925d8d842f25d4a5a33796a0f4a6725d8d042a27f250fe32696e4bf48b091852459a5ca3a5e8a6739adb190d42baada4bee44a9a42cd8b842829c8e51462991a2c2c8ad60c30a8967fe17aa634c1512ee4aba8a99aa69de4db04185c4f05fd9c42b681342da4cb03185a4a4b36716cd3daba3da820d2924cde528ddc182c9ddda0336a290207d4ec7a0533e65ee8db07d0f1e223dde8727156c402161f44e961675ad4ca5113607828d272409611f3687561e15f6085bffc086131254fd7818153f6fcd369a90bc72a7626e8b8faf7d844d8f07369890b461358988c88f352b0d6c2c21a9bc43758429bd317f82b0a184e4cb395dfeb5781ebe4afc838d2424e898f2b9f8318729190d1e9541c659d94042f2f9a8ed51329b58931d21392f6c4c22f42a98f48c907449c6f417a39d4ed91521e1bc5257b28c39c3061112d46fc6113e9eb22ebbf561630849f1f4b97a5a995f8f1142723295bf544cad1d649e6023083680901483b2f89e6410d7eb0111230326d03050202262e307ec656555e757baa9bc67cfe8ca8c954eea12031b3e48ca29a8b69cc2e7fca91e249b268da2648cf60b3678907c3d2aa8279516b6647c17c9495d9686eb585096b3a1862e92c465c5f4f8fe27b59ba0462e927d63a594f3e7e818dacb52d4c045b27fdc7728794b3bda19dc22715e469a0a7ffa832e10e9f13e78f0d8a1e7841ab648dc107d5daaa1e7654650d4a845f298baabb359cfa9c2223c66f023b1133568917ca165459adfb775df8347424ed4984572ab9c7f10a99f6ef69b218b64f7b48f497e6d9ad48838266ac422d15d2b47d3dde8aff63eb612356091b89dfb72aaa50cfa2e39891aaf483093994265d341d4e65c9128a2b3683b53b62259b6de7f5fe408a11e3c49d4604592a9f1cffa0be5ea9a1e6fc68e1f11b183448d55249a8df64c539bbe323b552468b4bb7432d637863315c97184a655a90eaaaa2cd44045825aca968395eed0ca9d22e967a3a994fa4afe97f60c354c91a0eb4ac9535d1b937c88642992da94fe67ee68f3e8f136f81e3f44d218bd41a8418ac4944ec88a412939ede11fefa3e3506314495aa7a4c6af267dbf8db0251e22088332c600e3c9281980411963ec101145529c9cba13f2ab2dc8f3ea502314493166cc53317d4e9d453350245f675257614385da20fb283d7ed0e0f707353e913432a724324e33c9bc3216a286279265c428f9d99933b6d389c40d9ae22ca6cb0e7bd1c12395b1014e24f8e59ce2870f37d17d6f22d9c359aa8dfbd131194d24eb5a0aaf1846c50cf24c247568da8cbacdfe97ab8189a4cd9ae9d45e741a5db1be4462bafd27993696ea3b2d915ca5329dc5f9cbb8b11249ba4cb4c9301584c7478964cb8c5963a5a066aeaa318924d3f51abdbeb2953d206280016227871a9248ac1ebba02bdf1a6a442241e97b0cfb5cdfb8b60e10849048ce30a3f64e8d77e5f788a411fb0c8d31f9e678bd231273690c22c4c87cc90fc974262e2485f3a4a56374febde0169236dbaeb3ca97106a2d249af6cacd29688fdf6621b137c51757ddecff6121d96a47eab97cc6aaee0a09d6192c06b11c947d5648bccdbc20e35aad5c5685a4d38b99d93d1512c7b28465cb1600a6909482fb5248b220bf6571949caa1f85e46c758f29cf5f5a0e8242b27952adb05da27ba227249ff4b421c45c9069434e48cef5512bae47adf34f1312752b7ed20c27f7478709c926458999e92cbe96b384a4242ac6f4b01de2474609892777229ff3aa836a92901c328dccf17862645390909c36db7de1beb134e50849df29a74bb76305bd182129ade6ecb4da9323f42224fcc89c92ca71e427d18990a0c39ea98e511573bc87901862d1c3e3dca2a92d8404614264ce0ea24f5d0e42a2e5b338272a064272ccf2519d9fb714f70709723b1fcb47a9143aea83c4b1dd169d97a9565500f4207143ecf755ac1c1506000f124dc7e9f870f5a4e3dc4582c95f8ca74c5d24ecbb253d2274de37e52241be3a645235e222419a55294fc2bc45e2c7abbdfdfcbda4648be4a06afeb2ba568b64bde839262aa345b298c74c6a76d3e7cc22e96314a164c9f609591609de9faf3247cfd25ab1488a7399c5e4bb64be0f7321072c12ccd47ffc98fa2ac77257c8f18aa4e8c9ebdaaccf3ce85d911862f3d7bd051d4765462103ab90a315493a55a8c747ff184c6b330a19c88ac45715a5e59e392617ad22712da9935b96638e393e430e55246e4ca2939b3e219694529198d2e6f094ff0839509174bde1f305f31c2f9446c8718a0413a951f34a88137275420e53245fa57509cd3fddb1538aa49c4c73f76ca7a818fd0f1e325826e42045e2678f59b458bc3131c72892fdd377d4eb050f4aca831ca2488ed169465912222d43236c6c3448b4c34081088d1ee40885df2007289235e82bfdb1149bf33f9170f9479e484e42c7706395ca4c7e3a91a849edff79290fab0d27923dc9f8165ce308a1c74decf144a611329bfb270c148894914313c956659fd99d53473c3932913f7260c28c1c97483831bae3e5f0aa3ba611b6ee33408c9ed121c86189e773fed59854a8ac549e1c9548feb469a62e6ffa8d332512a3bfe886c9fa7e771209236e32d7a81cb7af9444b28e740bad7efbd9733e68503a00a2e3ce6af03d7ec447d1c05d182810f14209392291b4d14566f9cd4122c1f557379d1851e9841e919c2f35559d389d7d1b472487be30d3a6a22919d74624a56097dbc4749a7a576f0f391891289f151e6ff62212375cbc9587581109ca3c85929e49e84e37118995f27df367ebd9986920626400a4063fcc202341658cc101101d203a402691031149da33dc6870edcf68394452ccf32474a99eeae4aa208721923e7f78f50f9f152239e64d661d63ea9199ad2b430e42247ae8cc50418dcbfb966310c9d631a69cd3edc3e677844d1049f163dea4de3e54501f1031c0d81c8148ca7bd9f9bfce985338c25683b203ede0715c50c61830e0c1c32c9e2207200c53de1f63a22c30e0c163e477a031ee0091e30f89a7da27a446db6c5230e4f043d206ad1e35de3e2409152f27dff2ea9cf9081bea18f93146097cecd85101101d20203a40748090f13f7cd44004206280019264991772f00119944eabbabc2472ec21871e124e850a27744688323d0f39f2607e1016be39c7d10f1e1235ca78f01b554188918d21c71d1254dc25152d6bf274ab0842424242d60e7c5671b12832e24b101d206180880106481d92f38615ed3fda1abaa6832ea35f746aaa7c751a72cc21398f0e9683fe78d66cca213937e6b864fadd34714852fd2ead193b76169d1128e480431d67b66f9b20c71b92be2fc63fd196a7a6e286c4dca67d9673d98f6c6798d186e4513ac76c4108997794d890189f7f9eec54d4e7d01a12ac3a89f3f859ffe9514352b60ae6a26ba99ad226e448831a66532af90c79a98386047139616da6429e278db0d9ef302e871c6748505f776a3b6552a72a6648ec98a2db087d9d83d564c85186c48da37efb3e274382aeb5eac7545923d730e418c3f14ec9728821f9d2d7eee88b1ebff367ec2871c8118644d35f7355a353f0cec9c07100440788b12107184adea5d473fc0b26f141e7d7b4170cb2363e25df303f6f17ca59da269465a86af03010e933a374e0026b66143244ca7af0f8513c071792758355a6f57293cd29fb337694330de4d842d28d1c9994650f1572682129e9303f326dfaabaf590b39b250b9c69d9bd6ab9be75cc9da331d4a44851791030b4976f9e94eab569d6a417480e800a940be21c71592bc3bcfb46b90e67103a243c4540d39ac90945d56b14e57d0f890a30ac9a244c6686c9b0ba21b725021f96f2bccdae363509f1f724c213946cf6549ac6eb08e8784d4d92187149236ad567d0e5b2a5c1f85a4cf99aa479d0c14126bd4522b456e70d7997d90e30949b37a9e619ad4477e203a40d090c30949dae5dac4856a958c1c4d48ce24a36eaaaab4b2252624e8cc54bf9da6749db984c4d0f89f5409b194364a487453d524e4978b942f471292ecffd2aea9090949662eda63673ebe7d84e4f21f0b565293caee8cb0610e235ca375c40919cf53724990a308499eed428d52ca1c4448d6abfe34aa2f6aca0f880ebf42e418427265b9bbd1b40a4a08d91c720821f962f359edc6c9520942a28586cffc2d1196424048d67cd131c2939f28f90f12bd74d810db3fb999f641a2773059b1a3bc657e1d060a446490a307c96e4af4ff747dd58773f0203983cee12f7549d361de45b2c6f7cf8a992e92f25ef651ab7fb52c73917c2753a6e74f32a9dce122d9466ce77ea86e9174223364f213dd4a3ab648da3c5a2c69bb56d94e2d8a9e498e075b0d2d12b3582eebcc8c1d2f398ba4a493e7205eff69940e872c927a4dfbfceda89c360cca8f94008e5824455d4fcdaa59845e122c924bc674d9d59e2f83e91549a1faac626c7f0fe3bb22d1b7b3ba3a75b84c42ad483cd1ce36ed91d70f6245c2a79454b652f0791dad2231f44ee74dda4b533caa48ae5ce3e9d1ff6a64938a44b5a4b7fbfda122316b544eeb0f4a957f8a848f1a4cbedde7c5b1982241c86d46cf8fe5a2538a44b7fe512d527c2fa4480e5a2949d3499d9a751449ee9ab1c2984c7d3151248dc7e8977139279f6d21fe175024a7b19cb3e9147384b83f91242da8d2e43f2793cf9e4838fd13e2b9d36b4ae944725bb00c312747085739911c7b21b49ce7184b6713c9f163906934cc94db9a484e4ae98a51695569cc4c24aeeb486d2d8d374298480a42d794c9206315e22592d3662b0faa6250b9b144a258f72595df6bbfb44a24c98ccb26cfc2a7e65022b194c8cae78abd239d4472ceb8bd686273c8d8bb0023804312c92bf671d327cd63a3c31189c412ea3dda265dd95443020e48247576db51669b52477f051c8f48de8a8d7e9e3e62df15030c102be07044a2f7b8668e5ad2a66c04de7a61d55562db0d703022f9923cb7505f4925a129634791010f323e046578004407881821213816919c54b3e4f6e783d4a411b61f658c312232f2638c19e4a5750087221263ccc945c3eaa552a2442477f8b5984afaa26e0811c95dbffb4154738824539f57fb649bb2bc0d9134c23c588844f3f0d561534b88e4fd0aa2d3643ea1f41b44b279ffa749175de23241240719c35c7ad2994e09ad0e3802915cdbdf99befd47e7c201883ff8a10138fa90001c7cd8831e1880230f78381c7778000e3b208e3a240e3acc410e7170000e38188e37b8410138da1082830d85630d0ec0a186341c0e34f878008e339881472a630c9132904104c7181680430c6100c302707ca17178610038ba60001c5cd88208027068210b581041008e2b58a10a07c0418502e09882140880230a507882139a20001c4c484a4fd3cc5ff22db7b34b484ebf79e63dc8b555eb870f0c8400e470282139e6df58c2bb84da7ce14842928a39af2bbbebe65191810309c9264534cde4739389236c0b224606467c140df07981091c4748b0f2a4ffa952d2929311923a945252bfe337464f11926ebc4afa86daa82011923a6e90155473a6d5780849a3ba3be860a6437c52088921e7cd63381d83a81784c40ef5304fad3357170e2024d98738adf074cad7c4f183a41ca4f76ed978d0230e1f24a6203fdec8d01c373d48fc9efd6e35919ab7e6040e1e24755ea5703af67b3bb553c58d5d249c527d1733c6b3481789412519339beba66d668a1bb9488a333288f3fc8c6e41236c288a1bb84810ba353548f59c64a89c75286edc22b1f4c5ac6af3af751a47dcb045a2cc5b90554179cbfee9c7f91e3dcaf0c0083c2006187a881bb5481ef3123d969dce830c2d924264aefdd14f32fdb348f2f0f473153ebe421ce1323c70768648193cd01c65917c9646fec878c76f3f1689e61f372c85c7fb0fc122f145ce4356d66842e815c9396e4a63ffb1da957145d2efac69b9a5ef914b2b124c66c75471846cad9a15c956f2bbd9759a64bcab48d29015e2365677f25415c999c2880fb2fed147988aa450f9296507993de60e15c9ee7936ed9c54bbd34e91b0217b4cbcbf4ca79822d145dfe95f8edf8f31669035780eb0b9066e9422b183b0942c888b59cd2b3dce28374891a0d3da05d131831cf17d103746916c5a9b2968c9f494ba16c40d512465544d1d443794a53114c921c3db7d4a3707e2062812755c4576d4c3a7fd3f91389657e47c8ea536e7f670c313c9a3943e9361312cbc637ab8d189e453d2939c34936a6215b156c40d4e24c775eed267794affb5b689e4b49e4fd5884aaa7b3a13313b49dcd04482ebe55bdf0b99f3310d80e800c900888e044407c8057474126e64225193050d994d63ae4498485069ab259b97ee542e917c72abdde3cd9648cca5b1e1e4a99cfee5066e54e2cabaf9cecdd0cab6b06e5022098e9fa8b40f461361180e05028130180c04038bf53302f31300000018249346e3f1802409d3ad0114000446423646282a1a301a161c180a864271301808064382302014060402614020164a295ae77503087c3af0fa046d24e88860f107522068c9071b51906e57751036975113c5567fc23f056706b4cccec4ba33e1c926c2360099004105e80fd02d401600390354001005404b00e9001407105a80fe00ba00640d902b409623c015673957b202440bf02ac0fafe6f02ce8f012d39c4e956ab05980b073056cb20464400b4a8bdcbc50eb8d216c098d9a3085a1e702594099cb704200a05bfaa18ba8107d104d0ba8279e5b3e711f437857ff6184c25a5facbf5e03408010afeb39070a29c894d0fe6518f7a8fd90b6af5002a48c9b793824d2f8d60faa2ac1451ae856fd5f069aafe74dd948fa28902e288188b49960934436c0bf7d6c8ff7c7667a79539b9b72c260288815a0a56060ffdcecdc4f9f12242a895e1eb1e806e49c9b64f841faf729a7516ba2f6a4c0d16f36891d245d4054a5bf39d7a20885a64dbd6be5feaa52e3f6b82a3097fd89fe57d139fe2c835e64b119059a4795e16520b81ad190f84bb809f24b30a840cdb993c524ebe144831e018a36d3b28652317379c08d6a448f5e5afa26b0160453680971d7ed8372ec335fc056257d59a2574170991db72bc9bfcc52c7b4387d091e4316c277f4e92cc0563fc39343cb3fc7cb544319b99693d946ccd08b876aff992f3cf5c9a506448f01804dd4f7c8bc0254096b95178de6f8f0528af109478bd25a86bc9d2a41f882f25a0442506f744be000e6cb289197bef9fc91f90f52bfa434fd38ff49351d3a5451a9ca87b16ed8eeda8774b40873a3907e1f7b94a7baf0f73117ec1e3a34d79c04e11d0962d4140487619906a64cbc68b21c1f5883f52f8724231b6272f42dcc9c32fe2e4bb2ad47c464e8a471b8e8c083a1e7af415f8547c35e6aa9bf95414e397317a4fc95bc0c1fdd626e94f24c6d0f263cbbf5ce0754395ffc6b84a74d5ab41530e38c6b8fb887f3e732ce333a3c27871d9a7b8fdb360a1b039e3450c19d0ba56bb2939be2bda54b6c969d2ccf25788738e52ea90b4c508ba9ebbea7c440cd43f441c3deb9a5e079b78a3ae3f39c8ae00cb77a5810da6d73cb65a831a7dcb06a48b2d8f36985164ea2251c2ecb0973ee0408626a372e86dcc5a5940f7862ac04e4298b0a56b6d27ec11e705135c839029f40903538963e6541239479056ee59aa329d60c99b2f6d05b077b886fa9c6215c5d28d175e3c0e3ee0421b58eeba033fe5157ab7b41669db5dd9b7e6eef7ea1e2e730e8256f704ef532264c8c850b006343292432095a69ec8db9fa17dee291f5dfda695a480e2082f2c409dabe49577406ea113f1461fe8258ae275e1648bb70832d59141ca9110b7e9172e962b8525ee49e34236ff9f64abd2bc241a04a19831e7762db2b75b0143e082a3926f141b7fa310116c9ff16aab5290540843318b5db80761509e8615aab140b149f74e5106f50a2f12928ef55ff9064ce022494fac05dabac1e64614748dbc97b57af3b65867a184fbe30e2533c38a3fcf2de2c0462b0100fa07a6f7036c0b700e0560a8248a2ab0657c3920318225d46bc42ed0322b82ec4a1ea69eded456e2213e3cc2310a4d2a93862b975785cab599fe056b17f0b45e87c2c239ee3f71511fe39b2359b528279c62e677d9833ac99d4a5c7bd8c40a8686ec9282184dedcda94c352e6b60bd9fcfd08e07ce975c056529695c4a0b4268231163c1917fa569438b525c1a6fb5b97f31ed1fef97ecc90a618769dda26cf47ebc635096bc833a4fbcf3a13fa6baebd7311334bc0117987808c2d53218fb3204773cf81368624a6f14b5cd44b1c046dea7ec3440ec42498a200e67326b84102d29966defba23c776d425e34b86dce28ac7e56256986b2e48257ce5ba522f6503b6932f951e5e542d22dcc5ea731688e2742fde40f53daa9117cfd611fbc51ab7eb02c9437620bc949cbd76930f9eddff78584cdc4844282f1e25cdbd8e2651871e026a5a6c234d3743395920ed0a1f312fc7a9db405fe8e649cdc20b54c4e12d7604068084ada213242d7e569b9a88ac5bf9d115295be40d3ec63292dff69db7543788dbffd49f16837fac722cf2e4c6cee344beb928a4bd2c77d94bb232cf596a7dc14be0b76ddc744c983892993c31f1561b383728ee95406c1c4492a2ab938a40fd2f84a934e00e9c1be594ecc749cf0e36f3c702993be936c427b1b2c18fb1a32b4672a598509a5c6181aa75081190f023a348d3414b415f6ba443caae8d308eb6f28bf14b87b74da7953c42173f9ffa4fe09b06d6875e940018a5b2d16085ff217aabc4bb24f4f83c7b03163cf799fe8bd230126286ba4a8316052cc567f6557eccd1ec68d89aaea88ab9265f21b305b8c8aec25ff5354873050ff1b280f88e3236e14496cc010042224942aae257ee0d52a866c03e35502f175e0a8a9c3ea1cd569097f7c26494fea7c1d2a6ccc9b9d2aecc977324767cb597de413df9b2f3cd90741ed73ef59c0c704ba0758ffb4b3efba1262a92dc798e0637e7576dd1bc7336b5d5cca1734fbbd63334c0ed08cf1dc53484f3130e56fe522632c8154aa911c645c41603f726c8d3c30d7d47afbcd38a7327311cd078508b30b0e3fea9014702995e964430ac8b12757afe4da033d5058cca2c6a7f6a4143233848e5e23ebe7e3ee899286f79b2e46cfc24d405b837639702727aae6e01ff5b8dc5cd0bb508c62bfd323cb34b9fe3189109ea0b554d573d6cee1955d00b5e68055d74e6eea277ca9471f797b84d1d83268f56b8b2e0c8395198a08c58739a042c23f1405576a09f9d58b6e69c57a63282ac217be590c3184b138ccc95e611bce8de22095ef4f6c4c6a735f486eccb38a847e4c8155e6821a8288c51f0a2d52b8539d931ff55f0a223a7143f9c13b4c88acd53b7222f568f5974c18b82fc5634c45a309dfb052f744f83c10b5da463f0420b2721cb2f38083b08cb810dfed0e04537ee4769a67495978719f276143107d3f6712201eb75eaaffe881cbc28e36a949e2965da31a33475f7e52951443be1420ce9a2aa897b46efe479f0420524d609abfe24fae08d7a9228d12a6fb41211254a3d43790cfc563d1caa33c13440a206e185eef64278a165cc75e290c113ea8167ec7a112cdffc00763905e2196561c011848bab805024788417eafcb14383b09a06bea84978d1ddb8125ed4eea61d9dbf962f975164c28baace0fa83ee209946a927d2dbc0f8e9f14131b685baf70a9b97dceabfd2eaa7437d122c4f8d85278d1cd3f8517dd2fa9f0a2cb43155e74cd1a30457d34b9566f568a4d8d17e9ee0780e28f2033944c7ae2f4293502f9cd46e2727f0d592ba949b8da39d6bc3ce58c47e24087688f88e99d7b10b3633ac800d3f81152193ff7dc219fe594accf90172a45746e9057b20a09cb2e8cf35f8eef59e0761ded1ae6453aa3c7d3664ef99d1834b02d8f384b02c71033a26a2cea4e3c830202f93cc1ba75334c36cb0ac6c98cf9aaacb541e2837319c5f8c5d66959a3c23632ff298bba2ff49790ed85c33338196920bcb9f385b699c078f2156d5647643f854cee828e7c6328dc4d62f8bad0e4976ec02e5d04ca4b20bb465162f7dd25d1b430714a38e0768b301f3d671834f48be96a22b6ebb9d11d036cb240220b0e67601b211faca28e2061ec0a26aa4fd0200a13cc7c99f81d70f0a46723be4011e72783d58b7d3dcff125fec8c37825bff82e7d6ef04ec06d820a8bb583f14125b127e0929105f70c11ec807af6fc60cb1498c8dde9b5d340aeb5fc75ce8339b435e39241445c9fee95a710f44b611e3e13c850e4655098fccf75ad4e4a296e792925d4a41805d48968ac9d10770d90786eff5248d2b5c064eb19416a742238b28498c1ab56dfc25b1b917194ce08cb6ab27f47da964f9384c3bac1dce45e4c8880b1c4f89cc821db21d0fe9c0ef379976ad9f74872e78953274c757212d63c1f5d303ea04e35d8ee15586f4a2653d7276b5bb13469b44610c874dee9bb678db7783908228e8333e4ac29b464ccfd4ffbb12a97ebeec8c5dc3036fa29f2c8e9a56438a152695a9c961b05ccd2ab6bc6f19b5832b13d3aa5b6f96990c421b1b71d7f922061102f2ec5c28044c1383646a05e02c1c6622f34ffb83d0e75ee6899acec7344799c32d8cf17945bec52e30745a9acbe742e7a8761b0bb2e6d97e83050d152bc84a6280222cb4f203c7e1f65f5418fa1833f786c65f595cd253129626dc986df9cd88df75633b4d7556d389f03d748e3c4bb89bc4644d5a5d32d30e5529a45dd7e76e4eaa5bcaa799a00d974855cee6726ff641ebfd2753dc9cd6f6cf89d420602483f4ae1035349e61d26e2e66931b01f9343375333d24cf38ae4448f72361c5a6c04df9e8d6e405ab917640f5a784bb98fa278f315c40cbf87933b6998e4b2aadcac302a3f5b833092de0ee36b7dda8991c7cc6f10020826a6ea3b1cc439ff719284a65a35ea83191816a8222116a9b2fec28f401918337a31edca2ab5dd2fd4da8e70dc9c42cf9f4f585df8916f4f42d41d2b72a86d892ccd284b6804521c1d4149667f250328cfe4e363593ad78b5f14a2eb73e358194ac5c57e25f2b28f64523cd5bc35c6ee2f52369ab568789e2f665821a057f6c227ae46012773b323932e2926b4de62297920d1043d2fa9ab68294ee987f822ebb3b348afbc3b367560a3ec8c5760c0564c4af1b659b6be15cf07be84f888430da2ba709cad266813d4d2180fb183c6b6cca2514607864a31447ec0616476ffb284f304df383b4beb1d6411d513075bfa7d59c557a9005b9ff58896398bbb9a270b1f2593a85fa3f612e4594b009654a304f27c23845bdd75827a5289eb00697c2155a8aa5e8c35964b1c86e623204354e41191888c80384d5a6a9d1418bae66e37cea70fbf13102f95c3a5f6bb681e928213220e655b319eacf275939dd4abd442a8a4d0236cf2b7c46ca00932608e7dd4c9cc385f10299f00805676750f6c98f365eeed22fc8a83b8b395e34781f501bdc8470cea066e92ad38d8fedb6781858d48569e6337414af00de9d7a7771c773ee8850fd7cbf8e3069a938c4b441f37db872875d610c2a6801d0b903940930b7c74640f6137bcb773982710e1a5fcd85290126b1cd411670e64256fd78cad4f032eca79a1d403e182b2a24f5c834173e9b9c96ea182c3d9e45d3364820ba4e0afcff403978de3f3d6ec2fc41199d4736768745762ffbdbc4417e67aa53e9894026c08149e084f2c425384e9231b333c2d9d90efd040aeeeca5b58d40289efab95b3605b549ea190877ae79445052842a13088b6c3a612ca38be938a630f610dde165754c4317ce05ca670b313009e374cbb3a08e234c077c2afe15ce60a12d64fa171a44cdf4d34df08756cc312f53b501de0c3c7b030a82c00e81b7585c685f1e94e152ae15a3a691b0c6b7c09171b6b3d8bba86357361335230ae4fc3192892ed53511b1ff55c3d285a275c3fe11e910028910608e943b034c6d7f068363f01a49bc4d4d52d0188208e91d04e17f051836dbfb475089a17f7080205c66a93ea03a93bdc02d352b1649c3c4860144816015d02bfb3e880a67e5c948009be783c631276c68a5106061e8e591870d50d89d4e3afadfb3e7a40075288002156d75c5b3f986d2399acb0932c0560f28d8915a5b5091d8ff8c69d490e69164ad75ec7092e51716966d73a479e814e009b57956f36f05d6f756f3c7d9f807b79d77933fade3e5c051c97f7996f5fc3ab000fc6f4bb934d923dd5913fe186ecc58260aefc0069ed225eb8d4f2cb83336c8f8106ad4ec7cf3705efedfdade41a0b04dc4dad606a3a807c59bfd0aa64465b5602bba1e1b2855e83b281e8fe82a34831a5036563079738de71b7a6c7d65820f32c8f47982a5d8d438746d019fd92d18e757a905313001f97d0b4d1b48f85457891aefb98a5577b78758448ced6b01aee53c7c44f32c1958883f7686fd30571d90794d4160ceefe142b2d0d2699c8ecb61f728d362e4bd8b34b6203ec2cb3c64e198c4589b0c05c79cbc53c86cbbab1531d5b18823dd033cbdbc3855715a59a4ca945ffc27b20755f9f014c12c3759151244770fc9b4861159162024a0741cce4a0de77b21845ae69f013ca51863e9a71569f047e7f0406fa386602d1d52655bcd3b71e1139c9cd68ce6c36bcca046647292ab58e25e6de7cbf74f5e6b6844a9baedd5b40f40904705d02470c7b63150267660e10df224679eb58e788535c45cb1a1bf864fcf51841bdc0209f6f2182c3a1826a26d2571660c406b11f59937c2340686e0dfe031b700bce219b73f686db5a0724630f5cbc24feff61bd1de6dec896c12f56fc4f8ad8c3bbf7313beffb9c59587e2041f7e11b807eb605edae43232d08ec1046e8379afa99d6a7c30315410009fb4ebf357ec9491411cf8e2c5bc9b30ea374210b7d9dace8685d61f9052f6dc0b69879ff266eaebb6bbb4007db20def36c4c8ab296928724eb640d647c8d67d263e92d7d797aaf58b1d0061c6d1a68612bf015e7dab8670cc9a42e5378c467ef7d6a47125ba16db17d1c8ff1f2aa48437e5418085939498d1db7a6d4455d6340186561ff06543ac06566158c6081aabd2ea597f9136617d7d9e402f47b698745525f09b5cef41bfe49bd4f1a3d5249b7a405b5cd8c5b52ab0e3a196b7363be46814d81b0a7c2cd575c146af07a210d5a5c10cf06504e8c82cc147758cfbc345bb42d82b5451077300378631a5910b5e059f8027224ca3c4ffd4262e97a8b614fe7f2bd223543939e504a4106c98516ee316498f8f97475a4a3a850b8d92f1d2c850d81bdb0fade5e2264e68de32f68b888cc92333cdb083a43a9cfd1b6f0cfc2643df9b8d5d4d4e1abd820d34a33a64937914b82fb4c8b2d7e9284c9ccf82a68c2543a626cbef19d052b23294b0e5d57913631a1ce2d0f180c4e6bcfb98de8c479dc42a2333c1f8149a5822cfac0e0f14160d8b4a3ff5d28277291a0a9113afd6fde4bbfbfbaed98d9130d90245f68023aa35f39adb7b3bf9a93294955a62148a5dea44afcbc0774b162e2b9f62e5590d1990ed25644f78f5998be65f72fbeec71cc556e48828201c22ee378451b1f00486072f545112ef6c77bf8e08b50bfcea2bbd54167c31ff6b718f414be047faded644331d2195231d5287c88c0a7436b7370f2078c1ccf9ae2365cb8a1f5a4c488867a7ddfe6a89c62593bb97b22c43e05fa24523527ba89f0bd166cf5b485581eea6f97f477b6f0c291553dbc8e40a42e6a7f465a818c39cc1037f3881023509ca11ab28021ab2af3236334a2f58d08604a2dd4e1d59448fc82134bfbd0a12d99d64cc1fc1cedad3a2203d503de8c53ea1d5388164ff2ae41c4b783ad2de0df309e961f0526ff689672805827de8fa594a727079f7f9baacad75603d9774aed2c55895c6305953a21ba6b5f519a76b7dff1eaf2e32e40b48f6fccb682e03520abfce397cb169402a0fa77266ebeee5d23823cf545385a8e7981678cc9b5aaeaf63af6905f25e36e2491030c7cb0686896a010ad88e1270ffc0627b8a6b0a7017be4145956c7556bb0379ed2b7d3d0d3e37cf9efb402125c955aacc649019ff313580aaafacc05f55648a627c1c0adeaaa41bdce7bcaf41ee7a0141d21aeb77fbfba5dbe5b6d63ef3ecf73d07e4b2b967f5d9ea19167ce0856d1708e9fb2784b7845824ed88b4eadb1dd61cfa5a8817eb0d75e897507f861296a3baa45e9497653cfc841493b5d417c0b6a011e91bac64ef2a13858552c59d0f2ed75e2231d71c5b47c0447e5b9c567938d39524924491e3d90023e9ee5191cb1110c3264df5cdf6c336d2a32c75b7c7fc4b0ee2bbc3abaef61ec96b441b4ff6ef45cde7e35a870742fe3c9041c672848ee950e69a217d8200144c4a50b39883b2f63d9221c5152cb9d126fdbb46eb643a9bae7e6ce095ce369c6b4691d3b996d7e5c5f0a2083f04eea8f65e5301e1c49c837b9ad6b69266cbad00831ef53faab9696a878848088a475d5bf525f903cd78208b806f9e65aa1090300a1e2a1eae50e9f12e4b23e87a731c527245eef75f11d9700e89decdd4f861ab4a6a1e7644153ce64d106d45ae6cd1644786829dbaca8e4c22315a4917ce88cf9058b965f17aee806144d6d1fc1d35c5faac3711644373074d95524827cca357e12aaa939408d17e74bccab278b7182031d192411a87ae9f88f88f8eed9692867a44b34396d8c957b70dec11597ffed2aefdbb97432392eaf14a8d17e6824049d40d626371623c137d43a44017b6e2754ebb8494492a179e3a8f5439de399946e59a48376aa4c27f097fecaa00833e20c5beafee22a6796e5d15646a9193df6b1cc8e35c27b61005180dbd71cd5db70a79c49fe3d9e9e78737829d42b87c6d637e4c64b74692bd7c75242d8e0d09a0ea6ff10da057afc4a07c536c4beb7f44571d6b39378ec15b76271f9b934a7940c1c8452f7c9439c22ffc30f442134b41c316d1aef3f4882ae32111688bfdb75f382b02e6de30ee8d7c4e85d5c8d388b78f2f21688ec719c3d6875c711d7f272b52f1a78fae1a28ece91271251d68ec5b864104d86b0a125d31ac88cb71f0067199b594c59614ab16e359034b9a7d93b50ca7ccfe253a23b3a7f6376948228b516ab3a321a0a7b2a40a557bbda0a98630756d0bdf8e604a581d28746aa7d574c8fe02b283ad1660cf78d02eae842d6e0cec5b9edc56480e3ba3d0519a0255b6966ad48badf19e8b43c2cab2f13ef56262b1cdfef1a20eea23880e02dabac87fec3508074d2043191ca2b632deefb1265dd5b6b86dd2da470de822137eb60f5e4b850f7328b452054f260f9aa7aaa1680b298d82051253c49db5449bd816731a554cb9a93275734e2532c2a498dca5ee51cdbb7a984b223be507133e3a95a3f27fba19e2ff6b19286e550b31ee610a785ce483a0967630ed2ecee684e18225bd2dadd453615f04dfaa722ac0a586018cdaed4b258e4834d51dd8a4693e1900b9a9341a9bb64af142b63e5ec05147623d41938bb43f9fb1b11c75b4599fc10f80103ff95dd304e6011edb9cd4fd1da0923258f27889346ac591fa1e57237533869014875bb8ceefa0956e0b6bcf030ff54eb49ae78702b7f0a92cc51794c4c7f4cb7aaf89432de0a16d63dcb2cba2aa41c995bee6a749c8488dc64fb9c0f95d187e1fa18f8c7fa02776e43e78bb0a741b4902591300f46d2bf3eaa2d303086a10b2928a9fca01a48bc7b14305398dab15934748f65804cb3e94ccab40c00db6a066595957e6e84ada6cf513b4cd86d6791a3544eb0598e515c79e673b43b41a4ccf933145015778b2de0103969975d820230017f09d8fda962686db3dc05cb5add34e415de4256f59a1fabdc65d60f6ca3846fed9819b455ea34cbc3f711f27d00a11ecf225b31141beb39d187f4ef418b8f8032c5587c1689c80c825d21bf9132cdd67d434151e70ba7ea4291b2974327237b0a25916bb027242fb22500cc7377775f62c02280a56ba2811716096d8233c72f55e000aa0deb5f14a451d80b3740541834e842258ac322d0f71b68fd76781b5881dfee2c87eaeebb8d8920a8049fac51ba4ff1a39e8c1a9804908f6cfc5f196ddd6463160a96fa2340913f36de427be519fd430fd66f539be4b4fb2907ee401b9cc06c2fde8c2ca0288fbb607bd5c4eea9822c82d39f4ac3446a7b8f87f178e3a223f2ba5a80464f3e4d34cf5c1ac0f958701f2685fbab7ffec20f3434b147623059d81f2524fa5d7aadbd0fe450d44a30d0dcaec034de8efaa32ea817a509a347d87e08997cb0300cf5d01c6ebd8b04bf975ba5ad1f2c04b82c08189242b293b090581cfde2cb3db282a8a16aaeaaac3a452d49564cd5a74a82818102b5144fd5ac380725684681a9b02aac0aafc2a8302a840aa542aa702aa40a53615558155e85516154081528da6802eca3d0490b5103d42035500d5303d5901aaa86aae1af016c901aa612387d7ff8a4c76f46aecab6cad3cab0cb564fabc033c8576ba31aeedb184d4a8ea11eb3f1d21bf3e6ef267bc37a037b23bd316fbe6ef237ac37b037f2c6efe6f4267943dfb8dd8037ba6fa8ab34d7dee961b00901d3265b68d3a021a495c4b4a71774fef195cda1233a79c3db316866185b8987eafa47c820a1511f9123e8e22d8ddcebc45eaf1939ac32382af75bbd2026091f451c79e52051c1a6b12ecaab198421d2277419c19b85e654775f1cc7378ec0d31d312dcd551d15ea7a3ecf35de13402494021cf99aa37d8db9135b9cf661abf9f9db7bd6dcc1738f638768e98759b7c1e88e4e8bf410fbf22215546d0bd62b581899aa60e9490e43948d4fcab577836a540d7a8cb446dd320fcc9fc1c6f9a0a7a41baff3919be6c5b0a658af4f981cab8eac102ad40be3e79b102204e53ec398f929e2a91645f40574d29870230019e03721e5f3e695f71933458a5d81ab6e452b230b0ce96a5488ed016acdab417cea978c3e9e985185926c9d67effe20d9c9acfc97c20736f2eddbdbde42e45081ae0ea5bcd04c5dbbccfb51ab5518f03375ffeae509ecf5db18c67d691d963de7addfb8f237d762c3109839ed3e86152ac944badb68d34ad9a93725e708692502b67d78c494394d86ba2d5d312e016cabff62064d9890cd157ece0acab1c33a7401692e3250835f40248b63d89bb91dec966ba6fa9d1125c3fdf4f8adfc597f392a907fdef8b06866a833cb1d1ae368a85931bd645812b66eb3c647411267a3673afc04ef2eb8637cf8083f96137de65d5d946965e3a6e6cd19a4ebc8713791a1d2f9d55b8ae80c72f558f15d8339abd10e3d90bcf84bf811c00fa0b15b3664e4ef0bab084904b57e229a154829fb2dd4d7ad35f2a0b435e3045c60dbe47e3172c4fbafa6d809efb3fe39205829182c737dc328f1eb27ec671a20520b6588ac02be30c1ba7d40b12765bcc81ce83cfdae09a991f00128ceb4a6d91e8fa5bfe9e722308081c0c77f233d3f8551daa8db600a67dc89bb30bcc20868ebe80bdba6add157384ffd25ff52507c0569af0f9a188d158a32148b9dfefe916d64567c9096ac2155533f1ee6ffad08633de629d1eacc4ee6b3debd78742a03e73eb8039eaba3b20d2a5eb52af7ba3b9b04d05b360cf1f6a9c67e87b819a9724bb1c61d0ed438a14111788b41db4b7d717b77030c426608065c2a91135311ffd0f8d8832bcbe7d447f6d462b2be504ebf539bda5cda570f80fba5d7f80d70ecd57632d596099f4b77e5fe5cde32fcf5adda67278e1a37ae41b71d2977c078ef1a84044b6a94e7d5097844c554d46d71b98760b5369598351a5e83b9c4f10cc2484344652b8b40e6b0868edf3292d10354cce5898608a24cc63b880f03b4644481d1a8acd3fb03cd4c49f59f0057a6c1448490ead786523b4113112cfb266d824662a35a949302513ad5d66b70f5ac3715a024948622a81275a4602aa535530dca41c12815b5a436aaa9fa76055e322fb525a5767899865b22b1949cd4900c59204fe4804f0e20a15c4ed10c55a04e1490d25123d5515e0a4ae9a90db5500dea45052919a5a41855a5ae149cd2500bd5a05c1490d2512e8d56851d6009b6470ab4d5d78bf7e6dde6feb883778bded5dc8cbb689fa701739df7ccdd713bbfe1ddaaf7567bc06b2d25ee7d971b07cdbc9987b9353fe7d82c9cb5999dabf37d86cc9a79985bb37386cdca799bd7b93bffe9082da115344517e96f64240b05a6c63774385d59ef3c3da19a0ae9b6d907f5a0d0a5d673e1dc3efbe6eb32c4149b6a9e1c679df3f204662ad029d93bf56342a6cacc85e8246d7b7e9e314e39a6662a69ca9589239eda3f9245819f8918acd23a80fde65a0a5840d7a69147b4b3a9e9a8c9c020c6236cd91c99493ad6a527643ab303ccd5bd0b3d048887297239af2025db4f02d6163f1c5931724a14ba702ef98433b2fad8408e180a677c108868b441fd8b7dc1d82b07f3314014b91036abe86e490a5413afa8ea0bd41db5c4909679047b7924a51a77ae1b94941a9a8e90e68a7fbf8ef1b5ccfb00d0940ce5ecbdfef1a53df99a6a9d542a546c2804c9e794dc73b13e81c20c92d613855b6b16db9ade9c1a28e239dd5a3482e740268dfbc365f66d88d4e1c0ee7ef5dfeb118d20ae373ce8b0aa086cd0ad5c3ffc206a8313cae815e1153d50fe12702e316d28b6c2f3e51938a5eb926df8f06555dd3a2c249b9b8eb14914cdf87c252d1d8537317dc2ebeb57a9b675194b223755c61da120d4d1cf89cd4c3a0f46c4b191b20d8505df3305064d2b516b681f47286773912dcd5e81778af9fbbba94faca39bb238d77665b7564080d3b90217a1b105ec5ce53814fbe2467cd64494511d64a60d82d58c9ee5249375137a865acbaa7289d0349a41ee4b305a0579400ce22c94c1857b0641c02366aa11bb180b5cf93a853e197a8f0e52278de7eb18ab18ccf0827160d23862cb8705a8ac8227a6708a4d9e22f989bdcd0842dd9d6413b0a4d0685f0c84abffb2ab856ac4c6cc40db2f5532dca6ceee82b45c55668fe6519ea8b65747418417e476a49bd46b32fc652a34e4e1da011aa5a0b7fa94fc21c4953e65d086598e5c5baccbc3b566ec707c0987063aeb68695efd18cd51733452c7609cd6666235ad96bbdeac13d5f1cf49c07dcf57c0dde3c46c52bca2fcb8c75d32a1b9cb963ab11104efc64de8997c16556400098c609f2231036ccbffce341556bb2e952a47a90c16e637a54a357065fd79b49e09c6a5470c5de660a2a4141f83b034c7914065c35a0822157913db0d187d82b61dc68768a020e4a7833d4d79f22c9d2e8f355c3f537178cc0acd4bf6efea2439a787ab9fc87d0075b3af8b1fed427181081c0b2606b422301df20626b960ac64f7c6f41314b414b9a1e8f7a9455fe3f07f9fdf90c89cf90dbd1348bd2f906e041b1d5b523f6c2ecebfde9b620581a415291fc1ca1045e56670c6b2e47254dd65c71c81ac7fdc4a1c99bfed0216cebda992b2259afbeaf87b82417aecadd1d011a59590845ed9321f120a7f1e9877d2f77a8abb46703d7555d3c5adfe2247efbd61e03239c01e5a3046b45278c27db3de0cc275442a985c23044d6a445331c636f8f405d98bf82c85224a6e849192bb66c020ec54018af6b8054d4e7b8458d264b2cb260e6e59d0e4eae02680207366a5f92873c425586f7709c6d68b98f01bbe70e2eecdacec5171ad20716b2938fd983b5a08de11216c201dd9cc1bac64065a2a10db390c83ff9eb5f3d1d8c16c63a5757f53e3e67edc7fa09e931f1dd7dd5b4cac314cf966ffd42bc60a1826b88529f10acfd1ad78e40ecd1e3d51deb5808e1fde23a366a6c28642e1d1476247b4b591257ceb84e47eaeb7fe258939d90e7b58432f74c7926ad8b443d05bb9e9250465c1369134e8db9ee26b9708d9ec49b472cab4b0046a81dc0b6388d8b072d963f10498a8bde3b35ff8e13d4111551a889c5262338bc9bd54b250fcc543e03d5577f64ac6eb477d82fd2e72d41c1c95e0d1892b25939589961772a84e642f56125bfedcdc59719c52f6e2f80d16e90035755d9dc767ef24e09b59bb6524f5ca5ab79ae93be898006fd621bcac22bfed8dd17deaa95af6034232dfbd55f02e6f628c0065104619ce0e5ed359c1a212ec529fa03f72cb25c88a292d1b392f29539c660bf5d2f2edeb65beec995d3a7165353c8190e5fc7c5f2a84c896e7c2e31d2170ad0c8f2d4b1fc2abb1b55a9c3a9a7b7b3c7e7740c8795f3a85c875b23066381cdbe66e10a8a8f578503dad0fac1f9f3c5a2f527b0cd95342ee9590074b29e8c46992d5036fbf3fb4ba7e56f7f6ea2734377047883cd847deac3c00328700bf5c4991f59f7d84a7359801a181e28cb16a37d1cfaf0fb7aebc2d8aeec62b9c6aa776d235814d2b0392b9b467d395fdb393311042005118b7702707fb2fc386d1ae8a5a8feee04a056939f39258da7a0748023568013ae38a743250b46439fcffffffffffffff3ac246086dff7ff2a5944912f3162f9291008094524a29a5a4e4b807eab7c0d12f61ff1dcf0a29075707e7066df6c6a46ad43a1b6c37105a371ddea4fa1f1d74cc81988452b7be306a7bbe8d1c850469234721237190e3b2430edec5ebd668ce8c6bbc99502fd359f3c4a5916de42824a0711d7120e50c1e34c784b28aa232c48000074c00011a3468d0c001079292d9ed5a41758fc0c001461b8e30c0c85186053ade40127a6e5bf49db68a8e1b08dbfdb99f63c9783ad5d106527c0b1511b16dde311574b0814dbb96d3b8f6d2371b312da247c57442e814316e8cc101316e8c9118091d6b206bd98f6d2631eab3d3a10652aa8c1654ae585cd3db6013038c1b393a0dc4f4eb55276d2c6b9a6ae0e0468e3e42071a48e71a2b6317ea740c3a03f1b3454e4da84f413d662075a8aea031eca9ffa60ca4bd78154b08ad830ce4d691dfec239497c66320c8a8094b675f9e524c0cbd7bb597d57c8d995966906f99a356cea2d630904a451162ea173010b4c6982a9b8f68cdafe30b799acc6e5a9c5bdbd7aba5ca7d9a318b5c0a09c220e3043772e0f00e2f10538c9534a5f3d78a295d20c894e73253964acba90e2e10dc5b368ede1197a2756c8170217b32c306d9784b0d1d5a20558cd3a1973ad9e8b2230b64d94beb1baed14486b040f898c1f3dce47786b1e30ae4bc144d96ab4c58c8b195d06105e2efc991a737ada94d1548a99298d0419c5428db556edc5546653ce3a28d50fd954de719abd03105b2064bd263fa4e0a2493fa3925759d28903eb5074dfd3f2264caea7980060dae61da010572e73d2183e7372573753c81549b6e4bc991572a07b9c3092473cda3b23bea7e4c4137b2a309248f49e7f24c6ae7f8d960abc157061933f8008e1b65680007ca81830c780713d6ba6c2775b5e2926e742c61b9b1ac11b513cbef8a3153294456bc8c76aa0e2590c6627685f019e5000c1c1d49207f5017cd2fa69c71b242071288793d6b5039f8274dd911489d2aa9f014db53b7462075a568667896afa78b40948df9292b6664858840d2e15574f4a55927d9750c81fcfe76edbfa6e7ebe41e740881947547252dcd3b82401291569a6554f6a0391d4020280f1e96e7f482c564c70f88fdf1845dd569c5951d3e38c6edf66554ed545ee4ae62442873d3b89ed40b62e9370b4ab647d7d667f082d4a5ef1e2cdccaa3ec8319bb2007356115fd32c67d6c862e08ca74c6f2ec2932a54e2ec8634166d3a092d4d2d119b8207bc6878f5db1738ad68c5b10478f14a544865bdcadb620569e17d9a38478bdac9930a316e49276e2cad4c63bf61c300104f6460e32f0053368a1deaeb9c796e7bd66b8fa86190d2fba36586cb081316316a45c1f3aa77c3a5dc4772307193e4316e4f47f772533e4b758528c3266802e98110be27dbe11aa5fe2163ab020b876a6cfdfc157902a4e2cb5bdbeefc8b882686eeeb9779beb633ea31584ab957bb9114bcba23dcc6005b9847c7d909fdbe133cec38c5510f369ea78bd2055104dc9bdfb87e6102a4a05f9add26a0cd282cb875041525f594fc794ad713aa720e65a8e6357a25284c714c420b5a26dea38f2c2530ad20639214d4de9f4cb938254f177642a7514c41cbfb37e4d56f77a5190cfcd349b5a0b2d7f2808224a588f5efccaa601057147d68567eae7a42f9f2005115b5f325ea2b927c867516ef379a8fe742788f193742f916945c40972c6e7cf22fe326edb9b206a5f2cf55a32c620664d90e474fa8fce61e39b4c10f309752996be458609f279d2a7ae628455ba9720badee8ffdb1863e49620a8574aa71beed9568294d77feba3edbcb79420e50b7af5bda7f3863a09523afd9739f484563b2541cef9c2deefe5625024c86f7671ba2b46351112c455d32f15d9a7620a1f41da2acf98728a9db473fc8ea9cb37c78d20e713ea7533ce99528f1144936b965bbc2ef8c716416cd1144fa174cfa48a205529cba9b2891cb56a4622881d6b1ad2630511040bbb3ba664cc24473c04e964a9cddcde99759ad661862148a5eeb72e2a754d9b0a413ea1a972c6575fed89851984207de5331de38d2a18c38c4190d4c79dc52bb5e1db726f98210882d2f9ee5154ac4b7ab33787198120a86947cf365a6f6ed486198020e51232cce24f7f205e66faab4ffdde26f70339c885cbe7649fb49c7d2066967a68ad7aff857c20856cfc4f39b7787b6e0fc48e2aa29ba2cc0759d103393605b919799ab5d279206837cf20a4f5077de10c3c904aa8a7b861a19429ed0ec4134fd761ff3fb526d9817c41a76ecb99946f9bea404cb7edf53da66bba44077289ad64d9e47f8c11cd81dcee6623d463fc588e1cc82b9f4e664d67a553270e04f3f8a7846758aad48103c9f38d86a6e0d12a75de404c51454d68cc2fdebe1bc8a2b196e7cfd3f5fa3610fbfe6cef64da1321c306b298095525de4d74c8ac812cef37a34a29fd181b35102fc7785eaf9734904f8632e9e951d488d040127e5541678612273f03c934ce65cacadba67633907a4e83ced6a9538c9681b4a1443fc910a76e6b32103f882ca5abbaeb293b06528c7fa529c5fbd8480ce49fbb4f17ef4d851b0652793e9dabd4627d0503e144cac8faf968b9d42f10f663bdebbc5e0e411296ff4a7bd6161b0d414a615aa2753547532148196e4aa55eb7e910822036fb6b6eae3c0641dc38fb4bed6f5aadf21004393c6eeef1d6db986e8f401035269d767e368e8c6783adc618e9d0ce0141ccc93c494b3bea63521b6c961e7f20e97938fd7e9d5c947fa61e7e209f30656135c7ace169d53e10bedcb535c71465942903e1c107b28d994ad950f1b5bf093e80965603bc01460d3270a4c71e489b766eff745cef283df440dcec1e3b25f3a073cd79206f1ab9f17acd6e2b8a0782c50e4a684ed31d483e9fff3fdf886c2f3dec40eca467bd464c458d510d1e75200991d5d665bb5123c6d2816cf77da5527eeb4d9d1b297d0e0431ea6df67654838c1c48ed2f771a42c7529962832d0edb9890b131abc8065b221c881a5c543e1961329324f07803c1a2c76df9f5f878cd041e6e206a8e234c06a1a753ad618401060868d068438d0578b0a13cca36a64cd9600c78aca1cc913daf8b0f4b4b75dbadcab96dc458c5430de44f937df6c9b4c1e66920c6d0e1ee56a1f1a48b06e28dd478bafff257e10f789c81f8d153fc2fa5ffe3626cb0b519481752648d29fdcbd5d8602b033944cf42296df5de593d32906210a12137c5bf8f291b6c65309801183670141b1c028f3190bc7a763659acb30cb2c12a0682cdb697ee656120aac69bca8c3f9f29b3c1760364a3061848415b45bbf9daabd936d8f0c0b89123bf40f0542a47ab7f51db4136d8d850e0e105a25cee3f4fb242871d7174815ca1f325d1a672d09e31c6e002317d053d7bf969bef336d8ea56e0b105c2fd8acde77429a3220e786881a05ba3d6a5a453cc3b0bc4922a5bf929a6f5d3c4030b24f515621d3be68c95bb02c945e857ca4cda60eb14785881fc17da3ab64488f3d1d9083caa4054df944ec6989d3ee86cb069073ca840fc798f96a163bc3b1d1c63d8a8b137061835c8c0a12268410d0f784c81ac5d7172ce4e52207f8a4908bbb8416de87078448114eb9dd3eb27794081d89b6eb6e34abf7f8ec713887595b26cc8d24183d0103c9c404a49557afb58212e831e4d205bb61c479de98baa8c0d3c98e0b1040f257824a1a0d6fb7316f7ca176df8b1c00309c7a6cc7e558db7aa5a7ca953aa36432268418de3718443b58feccc1a3608a0a003619021c6077090011c380a09ce181aa0410307c68c40124266d5796e566d0d8c94d23ec0010e8881a3d880068d1a27068f229072520f326711767f9d0711481d65dbf4e56c76b9f61802494b8feef20f6fe23c237808819cb5229fab970b421fcb3bc91c4be901045265d9f76a0c173c7e40509a7cce74d6d175a9870fc86b4267e50ee7498ad7d18b6ecc655e344fc762c3348bbcee8fa584d2ed123a7841d670299a77ba187f2fbb20e7cc37917529958c4d17e4af9c39080b0d2fa2cfc5326ea3a5e92157a27be5dd3256596a37a9245c90f234587c5f6d9fb70e1863a8a08689d1710b526fb060418db759bcd8a0c316e452aadf4c9e522d4857957a7b357479660fa205d1bc53704b3185fb7016a454a774b44db2d737230b525ade12b51e734b46b120285da2f378cc117e820559a4f9694acb2bada457904fb68fccd532621a571064094fb9ed84a8c54b2bc8a2e6a5c2e72dff386a5690639e5aa9c9a7595fe8c62a8861a7738faa916944d9a10a724e51bc842a37153f0d1ae806d7b082e33cd25410d45c4d6a8e905141f2513975f6bb5cc10d42c729c839078d266c839f0a325310749c3b5d29eb1c638471021a346a188e1c638481a33b4a41ce2c3d2a2529928294531af9c94a899ecaa320e6b14f626a54980a1da220cf7630cb73a9b6df44061905a3d0110a8285d4d39a3e7aa93328086e776a939c0a2b42bb1d9f505ffc2a284d21632e9eb8ed4b6547c7bdddbcce3e652cf1d1441d9d205ca82054a755bf9d4983060d1a665c6e9451b88313e48d7921e2920ccd1ada445d79d5e61a1623cc67ef4dccb7c3a809f2093d97d227e1236f6383ad06601cdc80f799672688b1e52de752b966490813847715fdbd183d33e65c8298948f1673df5db9b0254862ca9229a1c2c8ec51254839edc77487b775ee942069f4529a4fe62641dc202fd7d36d6bd850120461ad9ad9a366ce498b84fb62e62167a59f77ab6a4968ef484f438298e31793c5e95bed340d1a3468d82388615f619a5134287f69d0404790a3757eb63dcf34ba77348298455caee74d93d6ca88bc62bcae4ab73aed64dc6aa4666d11a4f9faacf87e91d5e90e4590848c0f656f7a74ac2c11e4511f954a7dd64c9b5f30742082f81ba3c9b670baa7a28720e9f94a1eba640a42976c0b1d86205aa58fc769775072b9424721c8219a62328ec6f65c2204d1b62f3db309bbdcbf0b1d8320e850ddf1e9ae2048224e4ef99fae170dea0804515b4c7f264bd2b32d096da0061d8020c80c99552ac45d9a67836d8c1a33b881d86ea01641c71fc8dd2256795564d073fb819464defc63ba4ddf5a471fc841c5eef8b5b258bf381f88aec163534395759560ec8174629356ca78d8f0b41ec89b73ee5a4da5f489270fc4a093d5e89bcc0fa6e2816c9bfc7e73bcb5b4df52061d77209a0c9db32145d6f20863d06107724ebda5d77276ea96c318630c1346471dc8e209a4ec269a33d8fce8caec822054bddf324917a43ef916fbcadf65940bf28d5d5af598b29f081764cb95a7933edf8c9e5b904fc63ddbd61664d18c8fdf49d6c2af05395ddedfeab4a9538616a4f550b6f526ad2f3e0bb2e92473d44da3e4290b8214951644cb92805810bd3f7fce52531c6149002c88651f46a39cd6515d49c02b881b7477eb578e3f5949802b88223d8e8899fc125a49402b88f9e2ab9ecaa7e1af9200567019fd641415b40a82aa3ad11f45c347a40af2c577c58fa6e2754e2a489f797c63fe5141b8adb0a3e4a297a99c826ca552bda9b1f841c51404953fe50c7f9782245412aad2f6f6479714c4e89ed96b73aa1c94a320e67679b7201405e153c78b9e73a120e5e5ab8a139d45730e0a620895cad27327f5ce3f414c62f6bf84e59e2079c75cb75dabf7a04e90d4bc7d2acdf1ec469c20f87a0513ef6c82982a35e8a9ba1abd264839988cb7206df4fb4c90cfb3764a427452a2c104b162c63cbea92357bf04d9dcc39dd0da12441b2174ae54578214fa4bd998f0ce51a604e12cd4abe7f4de5227413c8dd3639a531c3d25414cf7594d357624483256369c5267765a4382f0a1b92e05e5352ad58f20a6d2496d4aa97604a932a8c9d47957eb7023881f2dd5886732593e3382e49623cbf5cac3687811a48fd974fe90a7692aac0852f9a694b3871c59164e04694fbd2d968c8a5b6144903d6990394d690f57e1431054d013a13445974fb321c81ab677a7fa93bdcd8520eb26253ba7df925166429045e8eac9dc1595320f8270daedb263d2cc62160441c4e5a5f697caa0e5401093d68b2906597a4aca802085f191324a78f618e53f9055df2fe53fd90fa49ca449cbbe31d644ee03b182921fbae1c3ee633e90d472d8d524437bee780f04992be9689a52344e7a205aaccc61354b7f0cca03f174d7a6bbd3ed13e1817432eaa5f4a4d5a1bf03e9b62c4c64ecc8ff7620c58ca263f9c9a0df5207f266ca9397eb3b4ea7036b75fb493c0752caaf29e7c664a2a4722069f6aa754f21a3d18d0349b56eb76712d138170e247d5b579951ae65ee1b88d675abe3f7b153b96e20e747315d996478cb6d1b483e7a3fea7de58adab281242fdbdd8bd640cebb9e2693d228975a35102e569d5fd491154a03f17f44febecc3b95d040f0aaa4acf376b8e80ce4cc985e64eeaee86620559a0f9b11bb19511988e13b8b3ead99ca52c8404a71993af926133086a27c14cf4987c5408ef964496a1c06624a7d23f46ab44c5a309075d327bded9e4b5bfd02b952fc05fdb9d764aa5e2057294b6146a5fc7f6a17c8ea9ae3c85d95c5a95c2095a91e99ec72e610750be4fe9ca7dc73aa055298a74cea7a65afd32c90b6e3e98bb984f7c8140ba4313521dd7ccd2fd32b102fb9e79f5558599215481d3cc75b89ec609e2a1074c5d80d4a66be77a84032551583e79aece5a740f04f6255f1c63df9522077a6bde5300ae41c3fb95e4e2be141819cc43e5750aa9f34f304626f4c9117a146679c40306da931f309b19637819845a6cc1c7b3281784acb2621db5c02b1f664529fe27acec9540241262b5f57cf9e622c93400ed2439952e2f2592a9140ccf6e75bcafd2a56790472d01b8bb14ba52c4a46207afec920d3062df1290229ec5ac9585a4a7d4304e2674d595fe343209a9e0dba72ccbbbe08815ce2ffc763c5f5de4120c578adbc3ff659622010c5e48f146ddf59da1f9054e552711d7e834a26c007a490213fc85257aef782986db4426b4487127941bebed75239edd4b15d10d37feec4f968eb7541905aa17ee14979b03017e459f7d320937041b80e5aad720be2cbe818a35d771ad982a8692e7cf8e4327f2dc8a683924dbac2e50aa205b1cd635c4b26a1b4b3208ce7f9ed7bbc964716a40c9deb1bfe73e48f05c1b7eb525232e3e9b020bafc48fdaeb6ac9a579072be384d2a9b8e67710559e3d65673b385502b083aedb2568ef0f3594192cb18725e448cd25741ae1357695bab823857a7829c5153bcd839133a4605c92bd47914bde8e79e82346b2ac2f375a63f4d41d0174285cee54a41d09f36c9e5a02305b16c2e3e8fec67d66e14c40ffae3b921ddc23a511054a69caae67dcb210b05e9359b963e3d28c8e95642770ea764fef60952cc49f595b65cbe1bf304e9528852ea39fa496e9d207756ba6c42cf63d9c609f28f8c61c4288b41cdb6096210215dc4699a10b36982983fcb5efdd583d25a2648a94c48912173b614354c90b3c7688c8958ae6a9720a8589c901b9339a7cc1224ddd8a11784cc49575609626d3075d23be4c51225086f2e1e2be796cfd124c8a91963d29d4b7cfe48829416a4c6d05255eb4482b81f32b97a8d9ef62141b6a41927de61cf328f20bfe90bdfb4388278e3f5db61fac3c78d20993a9953a647e70d234859b1de82ce5af245103428d59e52d694f6550449ac59cb25bd63be2682789d3f2f63ec1c224490dc4dc9ca2f9e0c396e802a3003188081030113c0114631638401830a20054c4000384a0e0550000012b0abc05905f0ea1e108107140d651ae680cf81a38c1b0508000e4c051200801c38ca30010102806050ca80c10c4a180810008e1b68460870804146494000fe900ab8000070e0303408208092a31c1a050840c951ce1837681c00000368c08c1b48065f72e0a8514301000800030e8060e408c3068e52a3c68c2fae70802fac8060e4a80118a78c1a35667c5105929a3befca1a8b16bf0deeb332cec440376ad4008ce39663066094e00b2ae44038a640861852403072dcb081238c1a35667c1105ac5103015f40c1005f3c8154a1826f1216a3c15c1b6c6488f14e20794c51b61734054b952610ab4753eccc998450b518480cde2f98400c5ded5b2adbd5e1808c170389b162203110c70cc0c87183bf5802a93773ae2f8d79427b9440d21d93aa1eff0eb2e7fb2209a420835785dfd0a0e15f208198ea6535eba7d42a1d1c3df8e2085ccb6d7dcda57e7a5c7d8a28f597935f18018b7dcbd7af6f17793bdd4fe7964dff69e96d30860668d0b0c118376ee817452078d4d918574ce68f415f10616bab305dbdd82c578bfb14f76ebf8bb381e08b2110a4a9900f599e835f2604b2a51473714185075f0481bcd9299ed574ca173c5f0081985464cc523168a6ecbff80131498ddb33d9357bdd54f0850fdee49eb22a96f22dbdd8f366e3bbf565bbab6ad45c3e3d6d15ef5c5e9046a55895b96fbb2b8a39f8d80541cf874d95e5f665cf1d7ce8829cd285516da22c459d3f724130711783bcdfa9830f5c90bfd257566d8e59f41644b770193f69ca36153e6c4150d75de22a8dce944b19344065d080542d88e2a34788344b6147e506a141a3dc40607cd082a8c1fe4f8ef58cfa4a7dcc82182ac78a501daaa72f36d8d60462a01c1f589705d1bdaf624e328b05410599948968e879d7c7860f5810d499fc4dd1e663ccb2cfe1e315c4dc8b69c16fec2e45ae20095d3a64c35775de741b3e5a411eb712978f51a652ffc10a52522de1e14963cb7f6cb0cd000c23c6187fb6ab2006f1ec1f4aa4888a7b7af850053946e6689f50b5085a50a38c8f545c22fb2ea790df19840a72e99cb1769fff4efd8f539cf74429b7156f738b4d6168a8a968c567c9eebdecc5dc49af76362b8c5b1c7c9482eca79d1fbc628c25e1c981a3061938c807298839a996bba0e3656ffb631484cfbdea0bda5f3e73ee4314c4dc9a528aa8db1ce6ff080529fdc95e5b7a4af800057933c957baa610a7e98f4f107c2ec68a78778e673c414cf2ffc3d7e5508daf1364b353d5e9b5357c70829c3bba4ade9d9cadf42608164326cf22752eddaa0972a560eaef94dafba63e324112fb1ce6a3c73f304174bff4a2f64cba8c2e413e537377f23c4ed86f09e27eae917b96ab52ae2b41ee4ca6b29ec5a42d6659f04109e206ffa4abc64a7f3d9320e9c752b1b32209725c0f2384de2c375f7f44c2d3bcb7db99b576d76d0b17349c68cdd7e5fa0724c8db1d2be2e92ceae91104d1398de59bf5b3df11c498edec93ba3482a092e6a5b439a67ece0882878e723f99d9f45e046154e73a358b22482a6a6ccf381ef54c04b9522e0dd99a7b3e460449efbedaf77405d31e8214f7e3f88c8a2fdb106453378d6aad5d512d043167d60a7d714f9e490892f636254c868320fb6a59d57d4910c451956e477c291064d3eb29a6282adca8122048c944de5fda874d3a7f2008cda684fc8f1fc81df4a6245b7a4c3d7d20c96c4a2de61c4ca7870fe44fed0eeae1375bbf075245d1d613d2fe34ae0782feabddf718fc73cf03c9f46ecc5eee8ad8f140f2bc5796347d2ca1df81bc9f72f8d0d881304aa94c3bba97abae035973eeb89729539c980e24e9a239b4c23a5a89e640365de35f673a1f72208c30e92efe6d9d83ce471c88e3151b3e68f9fca0f3010772cea0e779543b94fe7cbc81a4ad2327b67f34c89c0f379064acfc41e5d0ff41733eda402ad9fff8f5d96e96f3c10672cabbeed1e52bed7e3ed690a7bc76315a5503693c4753b64bf6514d0339fb46516ed9d9528a06c29f6679fdf6d41ecf1988793e6eb5f25866f19881e8692bef5b9fccfa4e19c839327ec5ee7059dd21033996cc6fab079d52ea8c81b0e173e7c970625a1d3110773c6ffa9119ac94c240d01fc64567c040700b956dcdb4dc96be4034a1b2ba79d26cfae305924e53e92c4e8d9dba40921d0d7a4dd606db7081d85eaa3ddf33c7a0b205b2c724dd5ae3ffa10572879295e7bb1b3aff8f2c10f3d3722d2775faf77f6081ac5959bab62b8728ffe30aa4f7bcdea5e265fee07f5881bc99e9738eaf792ef81f55200979a33b753e638bff410592dfe7181afa7479ee3fa650d4b9a0bd54f75220ea5d898c1e4da6da3e0ac494d5bee53c8a4a1e2810f3a4ffa8bfa9549e2710d56acff7e482c5182790c4a5dbcd15cc2c6a9a40ce94b3f77c5ebfd69940ce921db3e56a09c476bbef4a2aead7560259a365d39f47e6a87512086232be069991401069b984998eea948f406eff51b3efb137c546207597fa753ceb3d691148a229acc4fa770a2b11885ac272c86808a4d0d3b1dba92944290492d015435c8c371fce8f2010de7388ce795953363f8040ceef98563cc6c59a1f3f209ec555dc9c466ae5cc870f88333a37eaa9ace12cd30b6265cea2c454dc2c96e14585d9a82306591c0c8582208862180681f0dc461d3315482018401e8cc402311648d27a6313c0c0c722f178200e8743a16048140a0561180441180441100461280661188c9b39723e3f043bb0ae663e965a851c4582aa26018e1eca6f016d96a9110f3688d0e386778fa196c514f196adb6cb020b1a81df52d685d0a59b0b8db85e37b271940464cab2794cdf363e016d4e675ca0758136f526a103db2facaf5d46b40ba39cfd83dc7e43cf0a1b9363b48771ac77773f89515207d8116e05a8358ae022c54aca65886685fdd31137c0e1016f31e282137b5b72348860e2c1eba63a07edee7300274830ec00be1737a72964c10ac6ac51fb320e764de6479756ded9d2a0289947810730d35be5db7eff2b3dc526deb65b5a8cb207fcaa42fe8e0c87d6145629870b553bba99bfc2e4b6f56602721191b3d3429789756ac0bc24e2d369161a11fa637fd9ead5f609420b6c05b40302015534728fbd0078fd9c682ec64e8d72ab100830f7c926ef963988ac2134d4f839d165f8279f20420c40562e873603aca4b4c350695ad895834cc8bf2040485424e0504cbc481d20df33440cc65c246a29aa4610f71298e8d8305f41d609525b23ec438a0d9720ccf15e1162257cd0040f06a9f6438c16fb6969499b2eb148fd38b8fef6f48c65ecfe825a2b5b3960b7eeb5fcdb922583ae74899564992621fa7b4fee3c4ae4de2945ec079bc2a0e072030e4277ae8660c4eb6e935839132341699fc45ff982171323b7901871034de8a1cdcb044171cf712d2659881b3ef689b9f170cb7fa665288225f4916dfef66502d1a1c4d2b66dafbac4fd114b4c97e7cae1bac21f0b61dc50d783887b9af2f8404050327cd20b96b0f3e8efbc0d1e1dc4e9e3b204ad9ed0f33a84768b449cb16546f7c0a010e8567e5190f317cd45320fba183635d51005afa090b79ce10761141751b4ebedbc46a28be19a2d33fb9c7158f67da7a0ba6037bd9a7e9b4a7189a4d8b268b71a29ae0e1a0296674ba96f18c10ed0c93928e29a2353ca1fad2c5cf0aace9ee6c2a8bde12a5b313a100c17acbe41b10a3cd7ecd85ae843695f3a9e64c0b5f5c3de40d9cc55e0886ec78db94530738e2a83e0d2be46c3ab3148182ffbf5360ed1e2d02e1a7dadc10a3a31c431e505b9774048b8dbd7d805b7080978419f5079073eeecbf6af8e6b947b2e692622917cc82405dbb8a38d62259dd7aed3fac33a32c897eafe4bb9479fa437a2b5624e6edbfc4c9fb44db1f505df4a66caa411c8522461e77e4feea6a53a57dba1fa1221c9ddb777d7a5dee5d817bfee43a2a60848ee6d992da4449df47b60cce08d33fa5c869bb611413dcef81ec7d99f70da48873e656a34af750e2c77cc93e208c64ef985ee14f740e84ecf72b0b14c3f91d946363134d29081744538edc36f7ddc66587badbcf23bdeb699d91da3b8140f7998331fe71af63b96a751f23b2c02a4abcbf0d42131d54755ed2c254ee944cf8a6676c3ca294939bffa6c865152e92672d6c2f4c39e70f8319c7981fb88f886d3aa61a517e6a40bdcca867dc5dd95d63b1cc84e498f86c9b43e6c4973f5214d4dc726acfb609b88d77e379c59cc650b28d8683267bec835c5edd0ec5fbd8f1fe1f7366eedae53f561a5de91a1460ab424cd71751fc82c52919b389589253f2da4510e79d2d0456c6a497c8454d5ab05d0488112990bcb01a7befdb2ef77d4c5b215043cdbe054538f24d4a996688761fdb40e50a262e21984aab95cd9372536b44e69fe9cb09dbc33fa7c2dc7c7771b411761d325e7932028fab8bb5281222217cb09aebefa0dbb33db76fe8814da493351dafa29b157838f93764057f707225244263dc1b572840bfbd6fa43bbd1def12a477cfe99ab97a15b3585e1440a4f77ac9ae679e6f3350687b22041da66cff6c56c95d2cb311bf539b9536adb63ad24dd0726f7a6bbfab44b7738eab1dd5300a2e7e29ef5e1a7b0bf4f3791d07852406cf636c41a2a3dc4518578f2d0463265c927451d8f752d479cf986af41cc314edae22c7610cfd6ba1263a038f7266378a3c216d06cd8cc18e2a92e15d2efa089ee59d85e277841cd7f0cebe116c55c9e9f6a79910b14eaed2dda20ffd9c11d7bfbb3fc379571cdbd82f0b46adbdb4456988af21cff5f7fe28366f5ea9e13e9c220f82ea70280991d5ff124527c771531402a3c9f536a1248a0ff9360993f309844922d19c98cb0c76387c8d5804db3fd66169cb26658b09911af91b3621ee40eb0b58c60f5bcece88e3a5483e7c2d9e98d1d654ff234815c3fb061c1496493ea494294c4eeb3912793f63ccdca2a6a5a8438c3537b913badfd0a11035363cb7d1f9e668ed9b342f1b231493a3d26c5f034896033cd2da9969dc433be5f1969779af650609c3e37881bd570318fd214a7991cd44c6fa00d802b8c5764a2e1385d6c8d026c2e47f9c474c0f0b09c6ad4029416de9cb6f8485f29c3b3334e1fdab785867f2ab65a5e68c95338bdc2b93a59f20effa62756b52874a7782f780da5f9207859bb8a8fb3dd7437e96940716505a20b080d30d19e28449b92ee6084307250f908104b4f40b7a183b144c689886cc429320ca59340aa35e34585f60ee81249a083e7fc7767847c646edacc6b825c6634c9ab4c03974306d0570835c4155a62ec4c8153751ae858d52748f8860e12ec206dec84a0125ec61245653a74748544b8a8f63968b4b409bd1ed3dd910e30358f50042e437d638527458eb3bbb7d3447d8bfbcf74211f82e78a64dff273fb60b298d3d22037ff003312db68cd4f727e979b4218ccce5670d86bd16b24f4e137ba5ff20b213743ab6c5218158181735a1b4b3bc2c8ab45ef052d221eb13052a222f5c17c4847f3db6d1ed9ffdf1221f4007a74e3952cc66df3deadd6767c98983fb6bb208eac73ec4021a3335d8a1f9f1ef44c709bcc9f430a49b54f2150840e3fc4089309b1113bd5f4b191fd33dad66b15b38dd1a85d1b8c53bac2d42301d8680c99a77cd262931bf3f331790a80a698fe19008d01eddb077db480ee94356ffd69fe6d7dd8eb1285ed1b71d761d0824924daedd0e8a42f0930aad876465032f745fe46b370a968a679b74e38c5fabd891592447dbb857c71b813b190294eae2874c84cc42272eba73ba1993375e726c98964038b8c89e194d93236e3dac7bfd082c905a14163d15f1446cbcb41adfebc3a16c17046ba76e00e4fe3cca98ca159b2139f9a8e5ac1812817f3cdc09646b98c1b1a61568259d26a09b5b107ecb0115c31381b51041426c9734342f60f525948423384834ad52d218f5833ca1e09874a8aabab2fdde8421f188238b56f9b5d41a867e7620f0eb61fdca2827322ca82464f21b770910d27e2bf37bdf691b769e17149578caff532969458ba9176136f8ecad66d48c567ce5f0a5890263910d80979ecc3b84fdcc84f7ff85ff36734a1e49c21f6cc2dce2e10fbf13899496cf3aad5cf5847bbcb341901ae713628f37ef87b54b21e12e63408dd0745861fe046050e57ea736b310198028862279ec99c12ab5143e68798580a783d0b15e8e465e3af89df9e620292a9b6eb206febfca071ec57ada5e812f3de308dca2ced5c0fdfe3088b43c33938d98acd81b4efabeb8adaa5c8e9cf720913ca84e48075fecbc228c0732c52df045069bfe1843af86da8e4584adec1882c463c3608f82f4a911ee6036a831482f5028742698c62a4ef6ca8151a695e1af6f12ae8d1bf32f4ac0b36a4d2c0c3e0b032325d766c3c95e8e224a114bc7a64a75c1298229224898b463ab59eca9c354b6712b381946d32923e42799138bef837e84b12bca878f7510d3e220531167aefec730b8ea035066c33b2dbc8272d1d631e2e8299b8c97a56de60f02ca8c9db0a0d7a4bf1ab8851e7ca42f876c6cb357cbfc8e7a27798fc10ed65329028aa7a7b8b1202d39bf9fec9492d33b4cf26410a9cb489917b61bcc99436a8eb0b9fc45007c1068641e5047ec9d550e9a814c92be50d8165fbec453182011653b296c90bfbea83871df13943da24063c2ab9df56312cc23dae31c6013a11e25c7f71a49242e52216c4c2984049b3fcef51696a5e88c04d3d8e2309bb99ffcd33e3dcf3868950caaf38b77af88edf2b72e411e71528a650846161cf4ace2f01df087be9a79d989f8e296e541f42af3fbaddff8dac8a3e96a617b901ab699fbab83d501469894c6218b929ae0435069a3f276794f961f9c34084a07d50882146a7782658d3c67398b641ca69a23020309e7fe4b69b4d2fb3bf0629ae3fcb2d6b29502b11776239833925fc06cd2491c689a16d542cc0aa54a0182731fd749a0ec4de54b726d5fa80062f1ab8a76452808c5a07c18deda34e54ef6cfc90c01d2863c2720b9fee430b6745b4dbaeeac99134d8a3f935056e933db8bb5a834584327a9eb3bd618e55f988f33e5ec811997798a461cfc529435d61a9685f114b63013ae17f7f42ad463cb1e103732b2982448162aef8cb27c1e5512cce46a84e451f2f32234449382c8a839ec2c248baaeb0c7ecfb3032f48a235236c540b6af9fd868cf89d02b5b8bc7cb0e4e24d39f13145288901635a1f40dd11c7969c41b90b296c1170df6a74eae967c306ec62227154f83c4162d347f92567c1680fa339a6668ecbad31a41a0a0fa9a6f106ab336b9af5c4130729880238855959ff7c033f4856469dd5e20dedc53b05e0d7ebf3a04e87c19c608e4596fb192826f198a7a40aabc9ab5bbb38aed42f8e9bddc44307d5c2d785cd38d030fe3f6c29a11e233de998fcb5440ad41ed9419e73d5a1ba7f4c22d2049412e7a8db57800e42e60ed8f46c9f801f1aedea89673c7af0812488c8408ab3050ab522b295946bdf9d18b1ea42234eb22b07e8887adb6b5b8f7d4287094cc7b36aaa15b0219290378c7d4f243116258de915d54efd40e733ab83105fead5dbae0cadde1e555221f8bc02f540a320540b86d92303f49902906a3312d2009d52219bee2dd64b27b014ff9243d1d353de08e231423a09e58d4a34b8142add256db74d3a9e0e3d52c344e397188146db05dfe1db12e9c8cd68d0578199d3b85f34302945e46f9a4e8e4e2fe16c0ce58c9347160fe22c8b60edd0246220e34a62a6fc90ef768f512effa6baaaa3660272f0ea7f8cc2ad75092e5417278a8b632cb9d927cd1195d734055d11386fa460ec058f575c222e652565786aebcae16dd9c6ed9baa309dd67323f02172b5ea2ed72f0e2e89ab3dbda1b9bf73c5fb66eec5d5978357ce7eccae3cde417c36b8eee165ddcdfb279997665de65c474dc3c6f075dbcddc8f2d6d7fa329365131e75839a5ada2253d8151a2d70574b39d6b0b09ff98e2339478690a37dd74f44e71b3236f4282cc8082e64b50bea4fbe63b1a3724b74695d01c5206b4d17a69008d14dc622739e39b8dba96f1c3f94c0b29356f650a14dc19605050211a481f32f6c5265412f37204a6db8326a5d5137227280939aa23820cff3a58c065b251298b7db6879e9ba513093fa5413c434d091e5e85a738e8f0d5668a996635b839d590590eacc03775271755a891ae39824d986e61da856377d44b66440d5a0c0a0da1badd0cb57de8702a2271aacc060b3bdc618c2a1d51f03d20983c50d9a06f10dce18b019c4319037d062306720db20d340f233589a905499cf3ff89e108330fb401342dea0bc075b12b013ca23b0f380cbfa3e" - }, - "aura": { - "authorities": [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" - ] - }, - "grandpa": { - "authorities": [ - [ - "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu", - 1 - ], - [ - "5GoNkf6WdbxCFnPdAnYYQyCjAKPJgLNxXwPjwTh6DGg6gN3E", - 1 - ] - ] - }, - "balances": { - "balances": [ - [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - 1152921504606846976 - ], - [ - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - 1152921504606846976 - ], - [ - "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", - 1152921504606846976 - ], - [ - "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", - 1152921504606846976 - ], - [ - "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", - 1152921504606846976 - ], - [ - "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", - 1152921504606846976 - ], - [ - "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", - 1152921504606846976 - ], - [ - "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", - 1152921504606846976 - ], - [ - "5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT", - 1152921504606846976 - ], - [ - "5HKPmK9GYtE1PSLsS1qiYU9xQ9Si1NcEhdeCq9sw5bqu4ns8", - 1152921504606846976 - ], - [ - "5FCfAonRZgTFrTd9HREEyeJjDpT397KMzizE6T3DvebLFE7n", - 1152921504606846976 - ], - [ - "5CRmqmsiNFExV6VbdmPJViVxrWmkaXXvBrSX8oqBT8R9vmWk", - 1152921504606846976 - ], - [ - "5D7daAUwyvuWNhJYXWXsjdAtfbPwuJopY6bACSCnB4BpoVYf", - 1152921504606846976 - ], - [ - "5D7AQFgLc1aE1oQbKTnVPN49BofZSifxtDtSvBSmVTeC9B9G", - 1152921504606846976 - ], - [ - "5GGebFFAcFK2Dsps2MuZSSqt9r4Zfd3ndrddXC7tS99ERuMg", - 1152921504606846976 - ], - [ - "5EEevrvMbd6vGjZzxQynyxN5KDPZwVDYnBUbUvvCg5A9GKWB", - 1152921504606846976 - ], - [ - "5GUBy8ZTAUNKBSXwCPCQTE4e7NanUch9if28iPjeJZNePiPR", - 1152921504606846976 - ], - [ - "5Cci1uvznfw7T1oWcrAGH3ZbRAF5BT2ieswYRU7GvvWsV4Ec", - 1152921504606846976 - ], - [ - "5EcZSDsxcow7sBFH4FSwPbYxsm2g8MpFeszydDYxHuteyS3y", - 1152921504606846976 - ], - [ - "5GeVCauvBNruAcUtmaFgW9yPZUbfzHvCkL32o8GBW9im5rL2", - 1152921504606846976 - ], - [ - "5Fxyb7LYUm8sQx5E5HjyKTHk18VCgdTaJFKNAgzakFz8AZow", - 1152921504606846976 - ], - [ - "5CcjDre6c9z79eFnEUj4TBocsaRkdrzwN4gNiQpGAYsbWFqH", - 1152921504606846976 - ], - [ - "5G3hv49pXnDxwhoiz8CXRfLfBhT6DwokgWX41Yw1LANKgjdp", - 1152921504606846976 - ] - ] - }, - "transactionPayment": { - "multiplier": "1000000000000000000" - }, - "sudo": { - "key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" - }, - "sharedStorage": { - "approvedCitizenAddress": [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", - "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", - "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", - "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", - "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", - "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", - "5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT", - "5HKPmK9GYtE1PSLsS1qiYU9xQ9Si1NcEhdeCq9sw5bqu4ns8", - "5FCfAonRZgTFrTd9HREEyeJjDpT397KMzizE6T3DvebLFE7n", - "5CRmqmsiNFExV6VbdmPJViVxrWmkaXXvBrSX8oqBT8R9vmWk", - "5D7daAUwyvuWNhJYXWXsjdAtfbPwuJopY6bACSCnB4BpoVYf", - "5D7AQFgLc1aE1oQbKTnVPN49BofZSifxtDtSvBSmVTeC9B9G", - "5GGebFFAcFK2Dsps2MuZSSqt9r4Zfd3ndrddXC7tS99ERuMg", - "5EEevrvMbd6vGjZzxQynyxN5KDPZwVDYnBUbUvvCg5A9GKWB", - "5GUBy8ZTAUNKBSXwCPCQTE4e7NanUch9if28iPjeJZNePiPR", - "5Cci1uvznfw7T1oWcrAGH3ZbRAF5BT2ieswYRU7GvvWsV4Ec", - "5EcZSDsxcow7sBFH4FSwPbYxsm2g8MpFeszydDYxHuteyS3y", - "5GeVCauvBNruAcUtmaFgW9yPZUbfzHvCkL32o8GBW9im5rL2", - "5Fxyb7LYUm8sQx5E5HjyKTHk18VCgdTaJFKNAgzakFz8AZow", - "5CcjDre6c9z79eFnEUj4TBocsaRkdrzwN4gNiQpGAYsbWFqH", - "5G3hv49pXnDxwhoiz8CXRfLfBhT6DwokgWX41Yw1LANKgjdp" - ] - } - } - } -} \ No newline at end of file diff --git a/client/consensus/Cargo.toml b/client/consensus/Cargo.toml new file mode 100644 index 0000000..f1d92bd --- /dev/null +++ b/client/consensus/Cargo.toml @@ -0,0 +1,82 @@ +[package] +name = "tc-consensus" +authors = { workspace = true } +description = "Client-side worker for Tanssi which unifies Aura and Nimbus" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[lints] +workspace = true + +[dependencies] +# Substrate deps +sc-client-api = { workspace = true } +sc-consensus = { workspace = true } +sc-consensus-aura = { workspace = true } +sc-consensus-manual-seal = { workspace = true } +sc-consensus-slots = { workspace = true } +sc-telemetry = { workspace = true } +sp-api = { workspace = true } +sp-application-crypto = { workspace = true, features = [ "full_crypto", "std" ] } +sp-block-builder = { workspace = true } +sp-blockchain = { workspace = true } +sp-consensus = { workspace = true } +sp-consensus-aura = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true } +sp-inherents = { workspace = true } +sp-keystore = { workspace = true } +sp-runtime = { workspace = true } +sp-state-machine = { workspace = true } +sp-timestamp = { workspace = true } +substrate-prometheus-endpoint = { workspace = true } + +# Own +dp-consensus = { workspace = true, features = [ "std" ] } +pallet-registrar-runtime-api = { workspace = true, features = [ "std" ] } + +# Cumulus dependencies +cumulus-client-collator = { workspace = true } +cumulus-client-consensus-aura = { workspace = true } +cumulus-client-consensus-common = { workspace = true } +cumulus-client-consensus-proposer = { workspace = true } +cumulus-client-parachain-inherent = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-relay-chain-interface = { workspace = true } + +# Polkadot +polkadot-node-primitives = { workspace = true } +polkadot-node-subsystem = { workspace = true } +polkadot-overseer = { workspace = true } +polkadot-primitives = { workspace = true } + +# Nimbus Dependencies +async-backing-primitives = { workspace = true } +nimbus-consensus = { workspace = true } +nimbus-primitives = { workspace = true, features = [ "std" ] } + +# Frontier Dependencies +fc-rpc = { workspace = true } + +# Other deps +async-trait = { workspace = true } +futures = { workspace = true } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive" ] } +tokio = { workspace = true } +tokio-util = { workspace = true, features = [ "rt" ] } +tracing = { workspace = true } + +[dev-dependencies] +cumulus-test-relay-sproof-builder = { workspace = true } +futures-timer = { workspace = true } +parking_lot = { workspace = true } +polkadot-core-primitives = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +sc-block-builder = { workspace = true } +sc-keystore = { workspace = true } +sc-network-test = { workspace = true } +sp-keyring = { workspace = true } +substrate-test-runtime-client = { workspace = true } +tempfile = { workspace = true } diff --git a/client/consensus/src/collators.rs b/client/consensus/src/collators.rs new file mode 100644 index 0000000..f5ac64b --- /dev/null +++ b/client/consensus/src/collators.rs @@ -0,0 +1,458 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +pub mod basic; +pub mod lookahead; + +use { + crate::{find_pre_digest, AuthorityId, OrchestratorAuraWorkerAuxData}, + cumulus_client_collator::service::ServiceInterface as CollatorServiceInterface, + cumulus_client_consensus_common::ParachainCandidate, + cumulus_client_consensus_proposer::ProposerInterface, + cumulus_client_parachain_inherent::{ParachainInherentData, ParachainInherentDataProvider}, + cumulus_primitives_core::{ + relay_chain::Hash as PHash, DigestItem, ParachainBlockData, PersistedValidationData, + }, + cumulus_relay_chain_interface::RelayChainInterface, + futures::prelude::*, + nimbus_primitives::{CompatibleDigestItem as NimbusCompatibleDigestItem, NIMBUS_KEY_ID}, + parity_scale_codec::{Codec, Encode}, + polkadot_node_primitives::{Collation, MaybeCompressedPoV}, + polkadot_primitives::Id as ParaId, + sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, StateAction}, + sp_application_crypto::{AppCrypto, AppPublic}, + sp_consensus::BlockOrigin, + sp_consensus_aura::{digests::CompatibleDigestItem, Slot}, + sp_core::crypto::{ByteArray, Pair}, + sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider}, + sp_keystore::{Keystore, KeystorePtr}, + sp_runtime::{ + generic::Digest, + traits::{Block as BlockT, HashingFor, Header as HeaderT, Member, Zero}, + }, + sp_state_machine::StorageChanges, + sp_timestamp::Timestamp, + std::{convert::TryFrom, error::Error, time::Duration}, +}; + +/// Parameters for instantiating a [`Collator`]. +pub struct Params { + /// A builder for inherent data builders. + pub create_inherent_data_providers: CIDP, + /// The block import handle. + pub block_import: BI, + /// An interface to the relay-chain client. + pub relay_client: RClient, + /// The keystore handle used for accessing parachain key material. + pub keystore: KeystorePtr, + /// The identifier of the parachain within the relay-chain. + pub para_id: ParaId, + /// The block proposer used for building blocks. + pub proposer: Proposer, + /// The collator service used for bundling proposals into collations and announcing + /// to the network. + pub collator_service: CS, +} + +/// A utility struct for writing collation logic that makes use of +/// Tanssi Aura entirely or in part. +pub struct Collator { + create_inherent_data_providers: CIDP, + block_import: BI, + relay_client: RClient, + keystore: KeystorePtr, + para_id: ParaId, + proposer: Proposer, + collator_service: CS, + _marker: std::marker::PhantomData<(Block, Box)>, +} + +impl Collator +where + Block: BlockT, + RClient: RelayChainInterface, + CIDP: CreateInherentDataProviders + 'static, + BI: BlockImport + Send + Sync + 'static, + Proposer: ProposerInterface, + CS: CollatorServiceInterface, + P: Pair + Send + Sync + 'static, + P::Public: AppPublic + Member, + P::Signature: TryFrom> + Member + Codec, +{ + /// Instantiate a new instance of the `Tanssi Aura` manager. + pub fn new(params: Params) -> Self { + Collator { + create_inherent_data_providers: params.create_inherent_data_providers, + block_import: params.block_import, + relay_client: params.relay_client, + keystore: params.keystore, + para_id: params.para_id, + proposer: params.proposer, + collator_service: params.collator_service, + _marker: std::marker::PhantomData, + } + } + + /// Explicitly creates the inherent data for parachain block authoring. + pub async fn create_inherent_data( + &self, + relay_parent: PHash, + validation_data: &PersistedValidationData, + parent_hash: Block::Hash, + _timestamp: impl Into>, + ) -> Result<(ParachainInherentData, InherentData), Box> { + let paras_inherent_data = ParachainInherentDataProvider::create_at( + relay_parent, + &self.relay_client, + validation_data, + self.para_id, + ) + .await; + + let paras_inherent_data = match paras_inherent_data { + Some(p) => p, + None => { + return Err( + format!("Could not create paras inherent data at {:?}", relay_parent).into(), + ) + } + }; + + let other_inherent_data = self + .create_inherent_data_providers + .create_inherent_data_providers(parent_hash, (relay_parent, validation_data.clone())) + .map_err(|e| e as Box) + .await? + .create_inherent_data() + .await + .map_err(Box::new)?; + + Ok((paras_inherent_data, other_inherent_data)) + } + + /// Propose, seal, and import a block, packaging it into a collation. + /// + /// Provide the slot to build at as well as any other necessary pre-digest logs, + /// the inherent data, and the proposal duration and PoV size limits. + /// + /// The Tanssi Aura pre-digest is set internally. + /// + /// This does not announce the collation to the parachain network or the relay chain. + #[allow(clippy::cast_precision_loss)] + pub async fn collate( + &mut self, + parent_header: &Block::Header, + slot_claim: &mut SlotClaim, + additional_pre_digest: impl Into>>, + inherent_data: (ParachainInherentData, InherentData), + proposal_duration: Duration, + max_pov_size: usize, + ) -> Result< + Option<(Collation, ParachainBlockData, Block::Hash)>, + Box, + > { + let mut digest = additional_pre_digest.into().unwrap_or_default(); + digest.append(&mut slot_claim.pre_digest); + + let maybe_proposal = self + .proposer + .propose( + parent_header, + &inherent_data.0, + inherent_data.1, + Digest { logs: digest }, + proposal_duration, + Some(max_pov_size), + ) + .await + .map_err(|e| Box::new(e) as Box)?; + + let proposal = match maybe_proposal { + None => return Ok(None), + Some(p) => p, + }; + + let sealed_importable = seal_tanssi::<_, P>( + proposal.block, + proposal.storage_changes, + &slot_claim.author_pub, + &self.keystore, + ) + .map_err(|e| e as Box)?; + + let post_hash = sealed_importable.post_hash(); + let block = Block::new( + sealed_importable.post_header(), + sealed_importable + .body + .as_ref() + .expect("body always created with this `propose` fn; qed") + .clone(), + ); + + self.block_import + .import_block(sealed_importable) + .map_err(|e| Box::new(e) as Box) + .await?; + + if let Some((collation, block_data)) = self.collator_service.build_collation( + parent_header, + post_hash, + ParachainCandidate { + block, + proof: proposal.proof, + }, + ) { + tracing::info!( + target: crate::LOG_TARGET, + "PoV size {{ header: {}kb, extrinsics: {}kb, storage_proof: {}kb }}", + block_data.header().encoded_size() as f64 / 1024f64, + block_data.extrinsics().encoded_size() as f64 / 1024f64, + block_data.storage_proof().encoded_size() as f64 / 1024f64, + ); + + if let MaybeCompressedPoV::Compressed(ref pov) = collation.proof_of_validity { + tracing::info!( + target: crate::LOG_TARGET, + "Compressed PoV size: {}kb", + pov.block_data.0.len() as f64 / 1024f64, + ); + } + + Ok(Some((collation, block_data, post_hash))) + } else { + Err( + Box::::from("Unable to produce collation") + as Box, + ) + } + } + + /// Get the underlying collator service. + pub fn collator_service(&self) -> &CS { + &self.collator_service + } +} + +fn pre_digest_data(slot: Slot, claim: P::Public) -> Vec +where + P::Public: Codec, + P::Signature: Codec, +{ + vec![ + >::aura_pre_digest(slot), + // We inject the nimbus digest as well. Crutial to be able to verify signatures + ::nimbus_pre_digest( + // TODO remove this unwrap through trait reqs + nimbus_primitives::NimbusId::from_slice(claim.as_ref()).unwrap(), + ), + ] +} + +#[derive(Debug)] +pub struct SlotClaim { + author_pub: Pub, + pre_digest: Vec, + slot: Slot, +} + +impl SlotClaim { + pub fn unchecked

(author_pub: Pub, slot: Slot) -> Self + where + P: Pair, + P::Public: Codec, + P::Signature: Codec, + { + SlotClaim { + author_pub: author_pub.clone(), + pre_digest: pre_digest_data::

(slot, author_pub), + slot, + } + } + + /// Get the author's public key. + pub fn author_pub(&self) -> &Pub { + &self.author_pub + } + + /// Get the pre-digest. + pub fn pre_digest(&self) -> &Vec { + &self.pre_digest + } + + /// Get the slot assigned to this claim. + pub fn slot(&self) -> Slot { + self.slot + } +} + +/// Attempt to claim a slot locally. +pub fn tanssi_claim_slot( + aux_data: OrchestratorAuraWorkerAuxData

, + chain_head: &B::Header, + slot: Slot, + force_authoring: bool, + keystore: &KeystorePtr, +) -> Result>, Box> +where + P: Pair + Send + Sync + 'static, + P::Public: Codec + std::fmt::Debug, + P::Signature: Codec, + B: BlockT, +{ + let author_pub = { + let res = claim_slot_inner::

(slot, &aux_data.authorities, keystore, force_authoring); + match res { + Some(p) => p, + None => return Ok(None), + } + }; + + if is_parathread_and_should_skip_slot::(&aux_data, chain_head, slot) { + return Ok(None); + } + + Ok(Some(SlotClaim::unchecked::

(author_pub, slot))) +} + +/// Returns true if this container chain is a parathread and the collator should skip this slot and not produce a block +pub fn is_parathread_and_should_skip_slot( + aux_data: &OrchestratorAuraWorkerAuxData

, + chain_head: &B::Header, + slot: Slot, +) -> bool +where + P: Pair + Send + Sync + 'static, + P::Public: Codec + std::fmt::Debug, + P::Signature: Codec, + B: BlockT, +{ + if slot.is_zero() { + // Always produce on slot 0 (for tests) + return false; + } + if let Some(min_slot_freq) = aux_data.min_slot_freq { + if let Ok(chain_head_slot) = find_pre_digest::(chain_head) { + let slot_diff = slot.saturating_sub(chain_head_slot); + + // TODO: this doesn't take into account force authoring. + // So a node with `force_authoring = true` will not propose a block for a parathread until the + // `min_slot_freq` has elapsed. + slot_diff < min_slot_freq + } else { + // In case of error always propose + false + } + } else { + // Not a parathread: always propose + false + } +} + +/// Attempt to claim a slot using a keystore. +pub fn claim_slot_inner

( + slot: Slot, + authorities: &Vec>, + keystore: &KeystorePtr, + force_authoring: bool, +) -> Option +where + P: Pair, + P::Public: Codec + std::fmt::Debug, + P::Signature: Codec, +{ + let expected_author = crate::slot_author::

(slot, authorities.as_slice()); + // if not running with force-authoring, just do the usual slot check + if !force_authoring { + expected_author.and_then(|p| { + if keystore.has_keys(&[(p.to_raw_vec(), NIMBUS_KEY_ID)]) { + Some(p.clone()) + } else { + None + } + }) + } + // if running with force-authoring, as long as you are in the authority set, + // propose + else { + authorities + .iter() + .find(|key| keystore.has_keys(&[(key.to_raw_vec(), NIMBUS_KEY_ID)])) + .cloned() + } +} + +/// Seal a block with a signature in the header. +pub fn seal_tanssi( + pre_sealed: B, + storage_changes: StorageChanges>, + author_pub: &P::Public, + keystore: &KeystorePtr, +) -> Result, Box> +where + P: Pair, + P::Signature: Codec + TryFrom>, + P::Public: AppPublic, +{ + let (pre_header, body) = pre_sealed.deconstruct(); + let pre_hash = pre_header.hash(); + let block_number = *pre_header.number(); + + // sign the pre-sealed hash of the block and then + // add it to a digest item. + let signature = Keystore::sign_with( + keystore, + as AppCrypto>::ID, + as AppCrypto>::CRYPTO_ID, + author_pub.as_slice(), + pre_hash.as_ref(), + ) + .map_err(|e| sp_consensus::Error::CannotSign(format!("{}. Key: {:?}", e, author_pub)))? + .ok_or_else(|| { + sp_consensus::Error::CannotSign(format!( + "Could not find key in keystore. Key: {:?}", + author_pub + )) + })?; + let signature = signature + .clone() + .try_into() + .map_err(|_| sp_consensus::Error::InvalidSignature(signature, author_pub.to_raw_vec()))?; + + let signature_digest_item = ::nimbus_seal(signature); + + // seal the block. + let block_import_params = { + let mut block_import_params = BlockImportParams::new(BlockOrigin::Own, pre_header); + block_import_params.post_digests.push(signature_digest_item); + block_import_params.body = Some(body.clone()); + block_import_params.state_action = + StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(storage_changes)); + block_import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + block_import_params + }; + let post_hash = block_import_params.post_hash(); + + tracing::info!( + target: crate::LOG_TARGET, + "🔖 Pre-sealed block for proposal at {}. Hash now {:?}, previously {:?}.", + block_number, + post_hash, + pre_hash, + ); + + Ok(block_import_params) +} diff --git a/client/consensus/src/collators/basic.rs b/client/consensus/src/collators/basic.rs new file mode 100644 index 0000000..3f401d1 --- /dev/null +++ b/client/consensus/src/collators/basic.rs @@ -0,0 +1,304 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::{ + collators as collator_util, consensus_orchestrator::RetrieveAuthoritiesFromOrchestrator, + OrchestratorAuraWorkerAuxData, + }, + cumulus_client_collator::{ + relay_chain_driven::CollationRequest, service::ServiceInterface as CollatorServiceInterface, + }, + cumulus_client_consensus_proposer::ProposerInterface, + cumulus_primitives_core::{ + relay_chain::{BlockId as RBlockId, Hash as PHash, OccupiedCoreAssumption}, + PersistedValidationData, + }, + cumulus_relay_chain_interface::RelayChainInterface, + futures::{channel::mpsc::Receiver, prelude::*}, + parity_scale_codec::{Codec, Decode}, + polkadot_node_primitives::CollationResult, + polkadot_overseer::Handle as OverseerHandle, + polkadot_primitives::{CollatorPair, Id as ParaId}, + sc_client_api::{backend::AuxStore, BlockBackend, BlockOf}, + sc_consensus::BlockImport, + sc_consensus_slots::InherentDataProviderExt, + sp_api::ProvideRuntimeApi, + sp_application_crypto::AppPublic, + sp_blockchain::HeaderBackend, + sp_consensus::SyncOracle, + sp_consensus_aura::SlotDuration, + sp_core::crypto::Pair, + sp_inherents::CreateInherentDataProviders, + sp_keystore::KeystorePtr, + sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member}, + std::{convert::TryFrom, sync::Arc, time::Duration}, +}; + +/// Parameters for [`run`]. +pub struct Params { + pub create_inherent_data_providers: CIDP, + pub get_orchestrator_aux_data: GOH, + pub block_import: BI, + pub para_client: Arc, + pub relay_client: RClient, + pub sync_oracle: SO, + pub keystore: KeystorePtr, + pub collator_key: CollatorPair, + pub para_id: ParaId, + pub overseer_handle: OverseerHandle, + pub slot_duration: SlotDuration, + pub relay_chain_slot_duration: Duration, + pub proposer: Proposer, + pub collator_service: CS, + pub authoring_duration: Duration, + pub force_authoring: bool, + pub collation_request_receiver: Option>, +} + +/// Run tanssi Aura consensus as a relay-chain-driven collator. +pub async fn run( + params: Params, +) where + Block: BlockT + Send, + Client: ProvideRuntimeApi + + BlockOf + + AuxStore + + HeaderBackend + + BlockBackend + + Send + + Sync + + 'static, + RClient: RelayChainInterface + Send + Clone + 'static, + CIDP: CreateInherentDataProviders + + Send + + 'static + + Clone, + CIDP::InherentDataProviders: Send + InherentDataProviderExt, + BI: BlockImport + Send + Sync + 'static, + SO: SyncOracle + Send + Sync + Clone + 'static, + Proposer: ProposerInterface + Send + Sync + 'static, + CS: CollatorServiceInterface + Send + Sync + 'static, + P: Pair + Sync + Send + 'static, + P::Public: AppPublic + Member + Codec, + P::Signature: TryFrom> + Member + Codec, + GOH: RetrieveAuthoritiesFromOrchestrator< + Block, + (PHash, PersistedValidationData), + OrchestratorAuraWorkerAuxData

, + > + + 'static + + Sync + + Send, +{ + let mut collation_requests = match params.collation_request_receiver { + Some(receiver) => receiver, + None => { + cumulus_client_collator::relay_chain_driven::init( + params.collator_key, + params.para_id, + params.overseer_handle, + ) + .await + } + }; + + let mut collator = { + let params = collator_util::Params { + create_inherent_data_providers: params.create_inherent_data_providers.clone(), + block_import: params.block_import, + relay_client: params.relay_client.clone(), + keystore: params.keystore.clone(), + para_id: params.para_id, + proposer: params.proposer, + collator_service: params.collator_service, + }; + + collator_util::Collator::::new(params) + }; + + let mut last_processed_slot = 0; + + while let Some(request) = collation_requests.next().await { + macro_rules! reject_with_error { + ($err:expr) => {{ + request.complete(None); + tracing::error!(target: crate::LOG_TARGET, err = ?{ $err }); + continue; + }}; + } + + macro_rules! try_request { + ($x:expr) => {{ + match $x { + Ok(x) => x, + Err(e) => reject_with_error!(e), + } + }}; + } + + let validation_data = request.persisted_validation_data(); + + let parent_header = try_request!(Block::Header::decode( + &mut &validation_data.parent_head.0[..] + )); + + let parent_hash = parent_header.hash(); + + // Evaluate whether we can build on top + // The requirement is that the parent_hash is the last included block in the relay + let can_build = can_build_upon_included::( + parent_hash, + &collator.relay_client, + params.para_id, + *request.relay_parent(), + ) + .await; + if !can_build { + continue; + } + + // Check whether we can build upon this block + if !collator + .collator_service() + .check_block_status(parent_hash, &parent_header) + { + continue; + } + + let relay_parent_header = match params + .relay_client + .header(RBlockId::hash(*request.relay_parent())) + .await + { + Err(e) => reject_with_error!(e), + Ok(None) => continue, // sanity: would be inconsistent to get `None` here + Ok(Some(h)) => h, + }; + + // Retrieve authorities that are able to produce the block + let authorities = match params + .get_orchestrator_aux_data + .retrieve_authorities_from_orchestrator( + parent_hash, + (relay_parent_header.hash(), validation_data.clone()), + ) + .await + { + Err(e) => reject_with_error!(e), + Ok(h) => h, + }; + + let inherent_providers = match params + .create_inherent_data_providers + .create_inherent_data_providers( + parent_hash, + (*request.relay_parent(), validation_data.clone()), + ) + .await + { + Err(e) => reject_with_error!(e), + Ok(h) => h, + }; + + let mut claim = match collator_util::tanssi_claim_slot::( + authorities, + &parent_header, + inherent_providers.slot(), + params.force_authoring, + ¶ms.keystore, + ) { + Ok(None) => continue, + Err(e) => reject_with_error!(e), + Ok(Some(h)) => h, + }; + + // With async backing this function will be called every relay chain block. + // + // Most parachains currently run with 12 seconds slots and thus, they would try to + // produce multiple blocks per slot which very likely would fail on chain. Thus, we have + // this "hack" to only produce on block per slot. + // + // With https://github.com/paritytech/polkadot-sdk/issues/3168 this implementation will be + // obsolete and also the underlying issue will be fixed. + if last_processed_slot >= *claim.slot() { + continue; + } + + let (parachain_inherent_data, other_inherent_data) = try_request!( + collator + .create_inherent_data(*request.relay_parent(), validation_data, parent_hash, None,) + .await + ); + + let maybe_collation = try_request!( + collator + .collate( + &parent_header, + &mut claim, + None, + (parachain_inherent_data, other_inherent_data), + params.authoring_duration, + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + (validation_data.max_pov_size / 2) as usize, + ) + .await + ); + + if let Some((collation, _, post_hash)) = maybe_collation { + let result_sender = Some(collator.collator_service().announce_with_barrier(post_hash)); + request.complete(Some(CollationResult { + collation, + result_sender, + })); + } else { + request.complete(None); + tracing::debug!(target: crate::LOG_TARGET, "No block proposal"); + } + last_processed_slot = *claim.slot(); + } +} + +// Checks whether we can build upon the last included block +// Essentially checks that the latest head we are trying to build +// is the one included in the relay +async fn can_build_upon_included( + parent_hash: Block::Hash, + relay_client: &RClient, + para_id: ParaId, + relay_parent: PHash, +) -> bool +where + RClient: RelayChainInterface + Send + Clone + 'static, +{ + let included_header = relay_client + .persisted_validation_data(relay_parent, para_id, OccupiedCoreAssumption::TimedOut) + .await; + + if let Ok(Some(included_header)) = included_header { + let decoded = Block::Header::decode(&mut &included_header.parent_head.0[..]).ok(); + if let Some(decoded_header) = decoded { + let included_hash = decoded_header.hash(); + if parent_hash == included_hash { + return true; + } + } + } + false +} diff --git a/client/consensus/src/collators/lookahead.rs b/client/consensus/src/collators/lookahead.rs new file mode 100644 index 0000000..611ff3b --- /dev/null +++ b/client/consensus/src/collators/lookahead.rs @@ -0,0 +1,543 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! A collator for Tanssi Aura that looks ahead of the most recently included parachain block +//! when determining what to build upon. +//! +//! This collator also builds additional blocks when the maximum backlog is not saturated. +//! The size of the backlog is determined by invoking a runtime API. If that runtime API +//! is not supported, this assumes a maximum backlog size of 1. +//! +//! This takes more advantage of asynchronous backing, though not complete advantage. +//! When the backlog is not saturated, this approach lets the backlog temporarily 'catch up' +//! with periods of higher throughput. When the backlog is saturated, we typically +//! fall back to the limited cadence of a single parachain block per relay-chain block. +//! +//! Despite this, the fact that there is a backlog at all allows us to spend more time +//! building the block, as there is some buffer before it can get posted to the relay-chain. +//! The main limitation is block propagation time - i.e. the new blocks created by an author +//! must be propagated to the next author before their turn. + +use { + crate::{ + collators::{self as collator_util, tanssi_claim_slot, SlotClaim}, + consensus_orchestrator::RetrieveAuthoritiesFromOrchestrator, + OrchestratorAuraWorkerAuxData, + }, + async_backing_primitives::UnincludedSegmentApi, + cumulus_client_collator::service::ServiceInterface as CollatorServiceInterface, + cumulus_client_consensus_common::{ + self as consensus_common, load_abridged_host_configuration, ParachainBlockImportMarker, + ParentSearchParams, + }, + cumulus_client_consensus_proposer::ProposerInterface, + cumulus_primitives_core::{relay_chain::Hash as PHash, PersistedValidationData}, + cumulus_relay_chain_interface::RelayChainInterface, + futures::{channel::oneshot, prelude::*}, + parity_scale_codec::{Codec, Encode}, + polkadot_node_primitives::SubmitCollationParams, + polkadot_node_subsystem::messages::{ + CollationGenerationMessage, RuntimeApiMessage, RuntimeApiRequest, + }, + polkadot_overseer::Handle as OverseerHandle, + polkadot_primitives::{CollatorPair, Id as ParaId, OccupiedCoreAssumption}, + sc_client_api::{backend::AuxStore, BlockBackend, BlockOf}, + sc_consensus::BlockImport, + sc_consensus_slots::InherentDataProviderExt, + sp_api::ProvideRuntimeApi, + sp_application_crypto::AppPublic, + sp_blockchain::HeaderBackend, + sp_consensus::SyncOracle, + sp_consensus_aura::{Slot, SlotDuration}, + sp_core::crypto::Pair, + sp_inherents::CreateInherentDataProviders, + sp_keystore::KeystorePtr, + sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member}, + std::{convert::TryFrom, error::Error, sync::Arc, time::Duration}, + tokio::select, + tokio_util::sync::CancellationToken, +}; + +/// Parameters for [`run`]. +pub struct Params { + pub create_inherent_data_providers: CIDP, + pub get_orchestrator_aux_data: GOH, + pub block_import: BI, + pub para_client: Arc, + pub para_backend: Arc, + pub relay_client: RClient, + pub code_hash_provider: CHP, + pub sync_oracle: SO, + pub keystore: KeystorePtr, + pub collator_key: CollatorPair, + pub para_id: ParaId, + pub overseer_handle: OverseerHandle, + pub slot_duration: SlotDuration, + pub relay_chain_slot_duration: Duration, + pub proposer: Proposer, + pub collator_service: CS, + pub authoring_duration: Duration, + pub force_authoring: bool, + pub cancellation_token: CancellationToken, +} + +/// Run async-backing-friendly for Tanssi Aura. +pub fn run( + mut params: Params, +) -> ( + impl Future + Send + 'static, + oneshot::Receiver<()>, +) +where + Block: BlockT, + Client: ProvideRuntimeApi + + BlockOf + + AuxStore + + HeaderBackend + + BlockBackend + + Send + + Sync + + 'static, + Client::Api: UnincludedSegmentApi, + Backend: sc_client_api::Backend + 'static, + RClient: RelayChainInterface + Clone + 'static, + CIDP: CreateInherentDataProviders + + Send + + 'static + + Clone, + CIDP::InherentDataProviders: Send + InherentDataProviderExt, + BI: BlockImport + ParachainBlockImportMarker + Send + Sync + 'static, + SO: SyncOracle + Send + Sync + Clone + 'static, + Proposer: ProposerInterface + Send + Sync + 'static, + CS: CollatorServiceInterface + Send + Sync + 'static, + CHP: consensus_common::ValidationCodeHashProvider + Send + 'static, + P: Pair + Sync + Send + 'static, + P::Public: AppPublic + Member + Codec, + P::Signature: TryFrom> + Member + Codec, + GOH: RetrieveAuthoritiesFromOrchestrator< + Block, + (PHash, PersistedValidationData), + OrchestratorAuraWorkerAuxData

, + > + + 'static + + Sync + + Send, +{ + // This is an arbitrary value which is likely guaranteed to exceed any reasonable + // limit, as it would correspond to 10 non-included blocks. + // + // Since we only search for parent blocks which have already been imported, + // we can guarantee that all imported blocks respect the unincluded segment + // rules specified by the parachain's runtime and thus will never be too deep. + const PARENT_SEARCH_DEPTH: usize = 10; + + let (exit_notification_sender, exit_notification_receiver) = oneshot::channel(); + + let aura_fut = async move { + cumulus_client_collator::initialize_collator_subsystems( + &mut params.overseer_handle, + params.collator_key, + params.para_id, + ) + .await; + + let mut import_notifications = match params.relay_client.import_notification_stream().await + { + Ok(s) => s, + Err(err) => { + tracing::error!( + target: crate::LOG_TARGET, + ?err, + "Failed to initialize consensus: no relay chain import notification stream" + ); + + return; + } + }; + + let mut collator = { + let params = collator_util::Params { + create_inherent_data_providers: params.create_inherent_data_providers.clone(), + block_import: params.block_import, + relay_client: params.relay_client.clone(), + keystore: params.keystore.clone(), + para_id: params.para_id, + proposer: params.proposer, + collator_service: params.collator_service, + }; + + collator_util::Collator::::new(params) + }; + + loop { + select! { + maybe_relay_parent_header = import_notifications.next() => { + if maybe_relay_parent_header.is_none() { + break; + } + + let relay_parent_header = maybe_relay_parent_header.expect("relay_parent_header must exists as we checked for None variant above; qed"); + let relay_parent = relay_parent_header.hash(); + + if !is_para_scheduled(relay_parent, params.para_id, &mut params.overseer_handle).await { + tracing::trace!( + target: crate::LOG_TARGET, + ?relay_parent, + ?params.para_id, + "Para is not scheduled on any core, skipping import notification", + ); + + continue; + } + + let max_pov_size = match params + .relay_client + .persisted_validation_data( + relay_parent, + params.para_id, + OccupiedCoreAssumption::Included, + ) + .await + { + Ok(None) => continue, + Ok(Some(pvd)) => pvd.max_pov_size, + Err(err) => { + tracing::error!(target: crate::LOG_TARGET, ?err, "Failed to gather information from relay-client"); + continue; + } + }; + + let parent_search_params = ParentSearchParams { + relay_parent, + para_id: params.para_id, + ancestry_lookback: max_ancestry_lookback(relay_parent, ¶ms.relay_client).await, + max_depth: PARENT_SEARCH_DEPTH, + ignore_alternative_branches: true, + }; + + let potential_parents = + cumulus_client_consensus_common::find_potential_parents::( + parent_search_params, + &*params.para_backend, + ¶ms.relay_client, + ) + .await; + + let mut potential_parents = match potential_parents { + Err(e) => { + tracing::error!( + target: crate::LOG_TARGET, + ?relay_parent, + err = ?e, + "Could not fetch potential parents to build upon" + ); + + continue; + } + Ok(x) => x, + }; + + let included_block = match potential_parents.iter().find(|x| x.depth == 0) { + None => continue, // also serves as an `is_empty` check. + Some(b) => b.hash, + }; + + let para_client = &*params.para_client; + let keystore = ¶ms.keystore; + let can_build_upon = |slot_now, block_hash, aux_data| { + can_build_upon::<_, _, P>( + slot_now, + aux_data, + block_hash, + included_block, + params.force_authoring, + para_client, + keystore, + ) + }; + + // Sort by depth, ascending, to choose the longest chain. + // + // If the longest chain has space, build upon that. Otherwise, don't + // build at all. + potential_parents.sort_by_key(|a| a.depth); + let initial_parent = match potential_parents.pop() { + None => continue, + Some(p) => p, + }; + + // Build in a loop until not allowed. Note that the authorities can change + // at any block, so we need to re-claim our slot every time. + let mut parent_hash = initial_parent.hash; + let mut parent_header = initial_parent.header; + let overseer_handle = &mut params.overseer_handle; + + // This needs to change to support elastic scaling, but for continuously + // scheduled chains this ensures that the backlog will grow steadily. + for n_built in 0..2 { + let validation_data = PersistedValidationData { + parent_head: parent_header.encode().into(), + relay_parent_number: *relay_parent_header.number(), + relay_parent_storage_root: *relay_parent_header.state_root(), + max_pov_size, + }; + + // Retrieve authorities that are able to produce the block + let aux_data = match params + .get_orchestrator_aux_data + .retrieve_authorities_from_orchestrator( + parent_hash, + (relay_parent_header.hash(), validation_data.clone()), + ) + .await + { + Err(e) => { + tracing::error!(target: crate::LOG_TARGET, ?e); + break; + } + Ok(h) => h, + }; + + let inherent_providers = match params + .create_inherent_data_providers + .create_inherent_data_providers( + parent_hash, + (relay_parent_header.hash(), validation_data.clone()), + ) + .await + { + Err(e) => { + tracing::error!(target: crate::LOG_TARGET, ?e); + break; + } + Ok(h) => h, + }; + + let mut slot_claim = match can_build_upon( + inherent_providers.slot(), + parent_header.clone(), + aux_data, + ) + .await + { + Ok(None) => break, + Err(e) => { + tracing::error!(target: crate::LOG_TARGET, ?e); + break; + } + Ok(Some(c)) => c, + }; + + tracing::debug!( + target: crate::LOG_TARGET, + ?relay_parent, + unincluded_segment_len = initial_parent.depth + n_built, + "Slot claimed. Building" + ); + + // Build and announce collations recursively until + // `can_build_upon` fails or building a collation fails. + let (parachain_inherent_data, other_inherent_data) = match collator + .create_inherent_data(relay_parent, &validation_data, parent_hash, None) + .await + { + Err(err) => { + tracing::error!(target: crate::LOG_TARGET, ?err); + break; + } + Ok(x) => x, + }; + + let validation_code_hash = match params.code_hash_provider.code_hash_at(parent_hash) + { + None => { + tracing::error!(target: crate::LOG_TARGET, ?parent_hash, "Could not fetch validation code hash"); + break; + } + Some(v) => v, + }; + + match collator + .collate( + &parent_header, + &mut slot_claim, + None, + (parachain_inherent_data, other_inherent_data), + params.authoring_duration, + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + (validation_data.max_pov_size / 2) as usize, + ) + .await + { + Ok(Some((collation, block_data, new_block_hash))) => { + // Here we are assuming that the import logic protects against equivocations + // and provides sybil-resistance, as it should. + collator + .collator_service() + .announce_block(new_block_hash, None); + + // Send a submit-collation message to the collation generation subsystem, + // which then distributes this to validators. + // + // Here we are assuming that the leaf is imported, as we've gotten an + // import notification. + overseer_handle + .send_msg( + CollationGenerationMessage::SubmitCollation( + SubmitCollationParams { + relay_parent, + collation, + parent_head: parent_header.encode().into(), + validation_code_hash, + result_sender: None, + }, + ), + "SubmitCollation", + ) + .await; + + parent_hash = new_block_hash; + parent_header = block_data.into_header(); + } + Ok(None) => { + tracing::debug!(target: crate::LOG_TARGET, "Lookahead collator: No block proposal"); + } + Err(err) => { + tracing::error!(target: crate::LOG_TARGET, ?err); + break; + } + } + } + }, + _ = params.cancellation_token.cancelled() => { + log::info!("Stopping lookahead collator"); + break; + } + } + } + + // Notifying that we have exited + let _ = exit_notification_sender.send(()); + }; + + (aura_fut, exit_notification_receiver) +} + +// Checks if we own the slot at the given block and whether there +// is space in the unincluded segment. +async fn can_build_upon( + slot: Slot, + aux_data: OrchestratorAuraWorkerAuxData

, + parent_header: Block::Header, + included_block: Block::Hash, + force_authoring: bool, + client: &Client, + keystore: &KeystorePtr, +) -> Result>, Box> +where + Client: ProvideRuntimeApi, + Client::Api: UnincludedSegmentApi, + P: Pair + Send + Sync + 'static, + P::Public: Codec + std::fmt::Debug, + P::Signature: Codec, +{ + let runtime_api = client.runtime_api(); + let slot_claim = + tanssi_claim_slot::(aux_data, &parent_header, slot, force_authoring, keystore); + + // Here we lean on the property that building on an empty unincluded segment must always + // be legal. Skipping the runtime API query here allows us to seamlessly run this + // collator against chains which have not yet upgraded their runtime. + if parent_header.hash() != included_block + && !runtime_api.can_build_upon(parent_header.hash(), included_block, slot)? + { + return Ok(None); + } + + slot_claim +} + +/// Reads allowed ancestry length parameter from the relay chain storage at the given relay parent. +/// +/// Falls back to 0 in case of an error. +async fn max_ancestry_lookback( + relay_parent: PHash, + relay_client: &impl RelayChainInterface, +) -> usize { + match load_abridged_host_configuration(relay_parent, relay_client).await { + Ok(Some(config)) => config.async_backing_params.allowed_ancestry_len as usize, + Ok(None) => { + tracing::error!( + target: crate::LOG_TARGET, + "Active config is missing in relay chain storage", + ); + 0 + } + Err(err) => { + tracing::error!( + target: crate::LOG_TARGET, + ?err, + ?relay_parent, + "Failed to read active config from relay chain client", + ); + 0 + } + } +} + +// Checks if there exists a scheduled core for the para at the provided relay parent. +// +// Falls back to `false` in case of an error. +async fn is_para_scheduled( + relay_parent: PHash, + para_id: ParaId, + overseer_handle: &mut OverseerHandle, +) -> bool { + let (tx, rx) = oneshot::channel(); + let request = RuntimeApiRequest::AvailabilityCores(tx); + overseer_handle + .send_msg( + RuntimeApiMessage::Request(relay_parent, request), + "LookaheadCollator", + ) + .await; + + let cores = match rx.await { + Ok(Ok(cores)) => cores, + Ok(Err(error)) => { + tracing::error!( + target: crate::LOG_TARGET, + ?error, + ?relay_parent, + "Failed to query availability cores runtime API", + ); + return false; + } + Err(oneshot::Canceled) => { + tracing::error!( + target: crate::LOG_TARGET, + ?relay_parent, + "Sender for availability cores runtime request dropped", + ); + return false; + } + }; + + cores.iter().any(|core| core.para_id() == Some(para_id)) +} diff --git a/client/consensus/src/consensus_orchestrator.rs b/client/consensus/src/consensus_orchestrator.rs new file mode 100644 index 0000000..04d9388 --- /dev/null +++ b/client/consensus/src/consensus_orchestrator.rs @@ -0,0 +1,63 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! The Tanssi AuRa consensus algorithm for orchestrator chain and container chain collators. +//! +//! It calculates based on the orchestrator-state dictated authorities +//! It is identical to AuraWorker and AuraConsensus, except for the fact that we re-implement +//! the ParachainConsensus trait to access the orchestrator-dicated authorities, and further +//! it implements the TanssiWorker to TanssiOnSlot trait. This trait is +use { + crate::{AuthorityId, Pair, Slot}, + sp_runtime::traits::Block as BlockT, +}; + +#[async_trait::async_trait] +pub trait RetrieveAuthoritiesFromOrchestrator: Send + Sync { + /// Create the inherent data providers at the given `parent` block using the given `extra_args`. + async fn retrieve_authorities_from_orchestrator( + &self, + parent: Block::Hash, + extra_args: ExtraArgs, + ) -> Result>; +} + +#[async_trait::async_trait] +impl RetrieveAuthoritiesFromOrchestrator for F +where + Block: BlockT, + F: Fn(Block::Hash, ExtraArgs) -> Fut + Sync + Send, + Fut: std::future::Future>> + + Send + + 'static, + ExtraArgs: Send + 'static, +{ + async fn retrieve_authorities_from_orchestrator( + &self, + parent: Block::Hash, + extra_args: ExtraArgs, + ) -> Result> { + (*self)(parent, extra_args).await + } +} + +pub struct OrchestratorAuraWorkerAuxData

+where + P: Pair + Send + Sync + 'static, +{ + pub authorities: Vec>, + pub min_slot_freq: Option, +} diff --git a/client/consensus/src/lib.rs b/client/consensus/src/lib.rs new file mode 100644 index 0000000..7fed00d --- /dev/null +++ b/client/consensus/src/lib.rs @@ -0,0 +1,245 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . +//! +//! The Tanssi AuRa consensus algorithm for orchestrator chain and container chain collators. +//! This file contains those functions that are used by consensus_orchestrator.rs structs and +//! and traits +//! slot_author returns the author based on the slot number and authorities provided (aura-like) +//! authorities retrieves the current set of authorities based on the first eligible key found in the keystore + +pub mod collators; +mod consensus_orchestrator; +mod manual_seal; + +#[cfg(test)] +mod tests; + +pub use { + crate::consensus_orchestrator::OrchestratorAuraWorkerAuxData, + cumulus_primitives_core::ParaId, + dp_consensus::TanssiAuthorityAssignmentApi, + manual_seal::{ + get_aura_id_from_seed, ContainerManualSealAuraConsensusDataProvider, + OrchestratorManualSealAuraConsensusDataProvider, + }, + pallet_registrar_runtime_api::OnDemandBlockProductionApi, + parity_scale_codec::{Decode, Encode}, + sc_consensus_aura::{ + find_pre_digest, slot_duration, AuraVerifier, BuildAuraWorkerParams, CompatibilityMode, + SlotProportion, + }, + sc_consensus_slots::InherentDataProviderExt, + sp_api::{Core, ProvideRuntimeApi}, + sp_application_crypto::AppPublic, + sp_consensus::Error as ConsensusError, + sp_core::crypto::{ByteArray, Public}, + sp_keystore::{Keystore, KeystorePtr}, + sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member, NumberFor}, + std::hash::Hash, +}; + +use {sp_consensus_slots::Slot, sp_core::crypto::Pair}; + +const LOG_TARGET: &str = "aura::tanssi"; + +type AuthorityId

=

::Public; + +/// Get slot author for given block along with authorities. +pub(crate) fn slot_author( + slot: Slot, + authorities: &[AuthorityId

], +) -> Option<&AuthorityId

> { + if authorities.is_empty() { + return None; + } + + let idx = *slot % (authorities.len() as u64); + assert!( + idx <= usize::MAX as u64, + "It is impossible to have a vector with length beyond the address space; qed", + ); + + let current_author = authorities.get(idx as usize).expect( + "authorities not empty; index constrained to list length;this is a valid index; qed", + ); + + Some(current_author) +} + +/// Return the set of authorities assigned to the paraId where +/// the first eligible key from the keystore is collating +pub fn authorities( + client: &C, + parent_hash: &B::Hash, + para_id: ParaId, +) -> Option>> +where + P: Pair + Send + Sync, + P::Public: AppPublic + Hash + Member + Encode + Decode, + P::Signature: TryFrom> + Hash + Member + Encode + Decode, + B: BlockT, + C: ProvideRuntimeApi, + C::Api: TanssiAuthorityAssignmentApi>, + AuthorityId

: From<::Public>, +{ + let runtime_api = client.runtime_api(); + + let authorities = runtime_api + .para_id_authorities(*parent_hash, para_id) + .ok()?; + log::info!( + "Authorities found for para {:?} are {:?}", + para_id, + authorities + ); + authorities +} + +/// Return the set of authorities assigned to the paraId where +/// the first eligible key from the keystore is collating +pub fn min_slot_freq(client: &C, parent_hash: &B::Hash, para_id: ParaId) -> Option +where + P: Pair + Send + Sync + 'static, + P::Public: AppPublic + Hash + Member + Encode + Decode, + P::Signature: TryFrom> + Hash + Member + Encode + Decode, + B: BlockT, + C: ProvideRuntimeApi, + C::Api: OnDemandBlockProductionApi, + AuthorityId

: From<::Public>, +{ + let runtime_api = client.runtime_api(); + + let min_slot_freq = runtime_api.min_slot_freq(*parent_hash, para_id).ok()?; + log::debug!( + "min_slot_freq for para {:?} is {:?}", + para_id, + min_slot_freq + ); + min_slot_freq +} + +use nimbus_primitives::{NimbusId, NimbusPair, NIMBUS_KEY_ID}; +/// Grab the first eligible nimbus key from the keystore +/// If multiple keys are eligible this function still only returns one +/// and makes no guarantees which one as that depends on the keystore's iterator behavior. +/// This is the standard way of determining which key to author with. +/// It also returns its ParaId assignment +pub fn first_eligible_key( + client: &C, + parent_hash: &B::Hash, + keystore: KeystorePtr, +) -> Option<(AuthorityId

, ParaId)> +where + C: ProvideRuntimeApi, + C::Api: TanssiAuthorityAssignmentApi>, + P: Pair + Send + Sync, + P::Public: AppPublic + Hash + Member + Encode + Decode, + P::Signature: TryFrom> + Hash + Member + Encode + Decode, + AuthorityId

: From<::Public>, +{ + // Get all the available keys + let available_keys = Keystore::keys(&*keystore, NIMBUS_KEY_ID).ok()?; + + // Print a more helpful message than "not eligible" when there are no keys at all. + if available_keys.is_empty() { + log::warn!( + target: LOG_TARGET, + "🔏 No Nimbus keys available. We will not be able to author." + ); + return None; + } + + let runtime_api = client.runtime_api(); + + // Iterate keys until we find an eligible one, or run out of candidates. + // If we are skipping prediction, then we author with the first key we find. + // prediction skipping only really makes sense when there is a single key in the keystore. + available_keys.into_iter().find_map(|type_public_pair| { + if let Ok(nimbus_id) = NimbusId::from_slice(&type_public_pair) { + // If we dont find any parachain that we are assigned to, return none + + if let Ok(Some(para_id)) = + runtime_api.check_para_id_assignment(*parent_hash, nimbus_id.clone().into()) + { + log::debug!("Para id found for assignment {:?}", para_id); + + Some((nimbus_id.into(), para_id)) + } else { + log::debug!("No Para id found for assignment {:?}", nimbus_id); + + None + } + } else { + None + } + }) +} + +/// Grab the first eligible nimbus key from the keystore +/// If multiple keys are eligible this function still only returns one +/// and makes no guarantees which one as that depends on the keystore's iterator behavior. +/// This is the standard way of determining which key to author with. +/// It also returns its ParaId assignment +pub fn first_eligible_key_next_session( + client: &C, + parent_hash: &B::Hash, + keystore: KeystorePtr, +) -> Option<(AuthorityId

, ParaId)> +where + C: ProvideRuntimeApi, + C::Api: TanssiAuthorityAssignmentApi>, + P: Pair + Send + Sync, + P::Public: AppPublic + Hash + Member + Encode + Decode, + P::Signature: TryFrom> + Hash + Member + Encode + Decode, + AuthorityId

: From<::Public>, +{ + // Get all the available keys + let available_keys = Keystore::keys(&*keystore, NIMBUS_KEY_ID).ok()?; + + // Print a more helpful message than "not eligible" when there are no keys at all. + if available_keys.is_empty() { + log::warn!( + target: LOG_TARGET, + "🔏 No Nimbus keys available. We will not be able to author." + ); + return None; + } + + let runtime_api = client.runtime_api(); + + // Iterate keys until we find an eligible one, or run out of candidates. + // If we are skipping prediction, then we author with the first key we find. + // prediction skipping only really makes sense when there is a single key in the keystore. + available_keys.into_iter().find_map(|type_public_pair| { + if let Ok(nimbus_id) = NimbusId::from_slice(&type_public_pair) { + // If we dont find any parachain that we are assigned to, return none + + if let Ok(Some(para_id)) = runtime_api + .check_para_id_assignment_next_session(*parent_hash, nimbus_id.clone().into()) + { + log::debug!("Para id found for assignment {:?}", para_id); + + Some((nimbus_id.into(), para_id)) + } else { + log::debug!("No Para id found for assignment {:?}", nimbus_id); + + None + } + } else { + None + } + }) +} diff --git a/client/consensus/src/manual_seal.rs b/client/consensus/src/manual_seal.rs new file mode 100644 index 0000000..969a472 --- /dev/null +++ b/client/consensus/src/manual_seal.rs @@ -0,0 +1,239 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! The Manual Seal implementation for the OrchestratorAuraConsensus + +use { + cumulus_primitives_core::ParaId, + dp_consensus::TanssiAuthorityAssignmentApi, + nimbus_primitives::{ + CompatibleDigestItem as NimbusCompatibleDigestItem, NimbusId, NimbusPair, NimbusSignature, + }, + sc_client_api::{AuxStore, UsageProvider}, + sc_consensus::BlockImportParams, + sc_consensus_manual_seal::{ConsensusDataProvider, Error}, + sp_api::ProvideRuntimeApi, + sp_blockchain::{HeaderBackend, HeaderMetadata}, + sp_consensus_aura::{digests::CompatibleDigestItem, AuraApi, Slot, SlotDuration}, + sp_core::Pair, + sp_inherents::InherentData, + sp_keystore::KeystorePtr, + sp_runtime::{ + traits::{Block as BlockT, Header as HeaderT}, + Digest, DigestItem, + }, + sp_timestamp::TimestampInherentData, + std::{marker::PhantomData, sync::Arc}, +}; +/// Consensus data provider for Orchestrator Manual Seal Aura. +pub struct OrchestratorManualSealAuraConsensusDataProvider { + // slot duration + slot_duration: SlotDuration, + /// Shared reference to keystore + pub keystore: KeystorePtr, + + /// Shared reference to the client + pub client: Arc, + + /// ParaId of the orchestrator + pub orchestrator_para_id: ParaId, + + // phantom data for required generics + _phantom: PhantomData<(B, C, P)>, +} + +impl OrchestratorManualSealAuraConsensusDataProvider +where + B: BlockT, + C: AuxStore + ProvideRuntimeApi + UsageProvider, + C::Api: AuraApi, +{ + /// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client` + /// implements [`sp_consensus_aura::AuraApi`] + pub fn new(client: Arc, keystore: KeystorePtr, orchestrator_para_id: ParaId) -> Self { + let slot_duration = sc_consensus_aura::slot_duration(&*client) + .expect("slot_duration is always present; qed."); + + Self { + slot_duration, + keystore, + client, + orchestrator_para_id, + _phantom: PhantomData, + } + } +} +impl ConsensusDataProvider for OrchestratorManualSealAuraConsensusDataProvider +where + B: BlockT, + C: AuxStore + + HeaderBackend + + HeaderMetadata + + UsageProvider + + ProvideRuntimeApi, + C::Api: TanssiAuthorityAssignmentApi, + P: Send + Sync, +{ + type Proof = P; + + fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result { + let timestamp = inherents + .timestamp_inherent_data()? + .expect("Timestamp is always present; qed"); + + // we always calculate the new slot number based on the current time-stamp and the slot + // duration. + // TODO: we need to add the nimbus digest here + let slot = Slot::from_timestamp(timestamp, self.slot_duration); + let aura_digest_item = + >::aura_pre_digest(slot); + + // Fetch the authorities for the orchestrator chain + let authorities = self + .client + .runtime_api() + .para_id_authorities(parent.hash(), self.orchestrator_para_id) + .ok() + .ok_or(sp_consensus::Error::InvalidAuthoritiesSet)? + .unwrap_or_default(); + + let expected_author = crate::slot_author::(slot, authorities.as_ref()); + + // TODO: this should always be included, but breaks manual seal tests. We should modify + // once configuration on how manual seal changes + let digest = if let Some(author) = expected_author { + let nimbus_digest = + ::nimbus_pre_digest(author.clone()); + Digest { + logs: vec![aura_digest_item, nimbus_digest], + } + } else { + Digest { + logs: vec![aura_digest_item], + } + }; + Ok(digest) + } + + fn append_block_import( + &self, + _parent: &B::Header, + _params: &mut BlockImportParams, + _inherents: &InherentData, + _proof: Self::Proof, + ) -> Result<(), Error> { + Ok(()) + } +} + +/// Helper function to generate a crypto pair from seed +pub fn get_aura_id_from_seed(seed: &str) -> NimbusId { + sp_core::sr25519::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() + .into() +} + +/// Consensus data provider for Container Manual Seal Aura. +pub struct ContainerManualSealAuraConsensusDataProvider { + // slot duration + slot_duration: SlotDuration, + // Authorities from which the author should be calculated + pub authorities: Vec, + // phantom data for required generics + _phantom: PhantomData, +} + +impl ContainerManualSealAuraConsensusDataProvider +where + B: BlockT, +{ + /// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client` + /// implements [`sp_consensus_aura::AuraApi`] + pub fn new(slot_duration: SlotDuration, authorities: Vec) -> Self { + Self { + slot_duration, + authorities, + _phantom: PhantomData, + } + } +} +impl ConsensusDataProvider for ContainerManualSealAuraConsensusDataProvider +where + B: BlockT, +{ + type Proof = (); + + fn create_digest( + &self, + _parent: &B::Header, + inherents: &InherentData, + ) -> Result { + let timestamp = inherents + .timestamp_inherent_data()? + .expect("Timestamp is always present; qed"); + + // we always calculate the new slot number based on the current time-stamp and the slot + // duration. + // TODO: we need to add the nimbus digest here + let slot = Slot::from_timestamp(timestamp, self.slot_duration); + let aura_digest_item = + >::aura_pre_digest(slot); + + let alice_id = get_aura_id_from_seed("alice"); + let expected_author: Option = Some(alice_id); + + // TODO: this should always be included, but breaks manual seal tests. We should modify + // once configuration on how manual seal changes + let digest = if let Some(author) = expected_author { + let nimbus_digest = + ::nimbus_pre_digest(author); + Digest { + logs: vec![aura_digest_item, nimbus_digest], + } + } else { + Digest { + logs: vec![aura_digest_item], + } + }; + Ok(digest) + } + + fn append_block_import( + &self, + _parent: &B::Header, + _params: &mut BlockImportParams, + _inherents: &InherentData, + _proof: Self::Proof, + ) -> Result<(), Error> { + Ok(()) + } +} + +impl fc_rpc::pending::ConsensusDataProvider + for ContainerManualSealAuraConsensusDataProvider +where + B: BlockT, +{ + fn create_digest( + &self, + _parent: &B::Header, + inherents: &InherentData, + ) -> Result { + >::create_digest(self, _parent, inherents) + .map_err(|_| sp_inherents::Error::FatalErrorReported) + } +} diff --git a/client/consensus/src/tests.rs b/client/consensus/src/tests.rs new file mode 100644 index 0000000..025ecac --- /dev/null +++ b/client/consensus/src/tests.rs @@ -0,0 +1,674 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +#![allow(clippy::await_holding_lock)] +// This tests have been greatly influenced by +// https://github.com/paritytech/substrate/blob/master/client/consensus/aura/src/lib.rs#L832 +// Most of the items hereby added are intended to make it work with our current consensus mechanism +use { + crate::{ + collators::{tanssi_claim_slot, Collator, Params as CollatorParams}, + OrchestratorAuraWorkerAuxData, + }, + async_trait::async_trait, + cumulus_client_collator::service::CollatorService, + cumulus_client_consensus_proposer::Proposer as ConsensusProposer, + cumulus_primitives_core::{relay_chain::BlockId, CollationInfo, CollectCollationInfo, ParaId}, + cumulus_relay_chain_interface::{ + CommittedCandidateReceipt, OverseerHandle, RelayChainInterface, RelayChainResult, + StorageValue, + }, + cumulus_test_relay_sproof_builder::RelayStateSproofBuilder, + futures::prelude::*, + nimbus_primitives::{ + CompatibleDigestItem, NimbusId, NimbusPair, NIMBUS_ENGINE_ID, NIMBUS_KEY_ID, + }, + parity_scale_codec::Encode, + parking_lot::Mutex, + polkadot_core_primitives::{Header as PHeader, InboundDownwardMessage, InboundHrmpMessage}, + polkadot_parachain_primitives::primitives::HeadData, + polkadot_primitives::{ + Hash as PHash, OccupiedCoreAssumption, PersistedValidationData, ValidatorId, + }, + sc_block_builder::BlockBuilderBuilder, + sc_client_api::HeaderBackend, + sc_consensus::{BoxJustificationImport, ForkChoiceStrategy}, + sc_keystore::LocalKeystore, + sc_network_test::{Block as TestBlock, Header as TestHeader, *}, + sp_api::{ApiRef, ProvideRuntimeApi}, + sp_consensus::{EnableProofRecording, Environment, Proposal, Proposer}, + sp_consensus_aura::{inherents::InherentDataProvider, SlotDuration, AURA_ENGINE_ID}, + sp_consensus_slots::Slot, + sp_core::{ + crypto::{ByteArray, Pair}, + traits::SpawnNamed, + }, + sp_inherents::InherentData, + sp_keyring::sr25519::Keyring, + sp_keystore::{Keystore, KeystorePtr}, + sp_runtime::{ + traits::{Block as BlockT, Header as _}, + Digest, DigestItem, + }, + sp_timestamp::Timestamp, + std::{ + collections::{BTreeMap, BTreeSet}, + pin::Pin, + sync::Arc, + time::Duration, + }, + substrate_test_runtime_client::TestClient, +}; + +// Duration of slot time +const SLOT_DURATION_MS: u64 = 1000; + +type Error = sp_blockchain::Error; + +#[derive(Clone)] +struct DummyFactory(Arc); +// We are going to create API because we need this to test runtime apis +// We use the client normally, but for testing certain runtime-api calls, +// we basically mock the runtime-api calls +impl ProvideRuntimeApi for DummyFactory { + type Api = MockApi; + + fn runtime_api(&self) -> ApiRef<'_, Self::Api> { + MockApi.into() + } +} + +struct MockApi; + +// This is our MockAPi impl. We need these to test first_eligible_key +sp_api::mock_impl_runtime_apis! { + impl dp_consensus::TanssiAuthorityAssignmentApi for MockApi { + /// Return the current authorities assigned to a given paraId + fn para_id_authorities(para_id: ParaId) -> Option> { + // We always return Alice if paraId is 1000 + if para_id == 1000u32.into() { + Some(vec![Keyring::Alice.public().into()]) + } + else { + None + } + } + /// Return the paraId assigned to a given authority + fn check_para_id_assignment(authority: NimbusId) -> Option { + if authority == Keyring::Alice.public().into() { + Some(1000u32.into()) + } + else { + None + } + } + } + + impl CollectCollationInfo for MockApi { + fn collect_collation_info(_header: &::Header) -> CollationInfo { + CollationInfo { + upward_messages: Vec::new(), + horizontal_messages: Vec::new(), + new_validation_code: None, + processed_downward_messages: 0u32, + hrmp_watermark: 0u32, + head_data: HeadData(vec![1, 2, 3]) + } + } + + } +} + +#[derive(Clone)] +struct RelayChain; + +#[async_trait] +impl RelayChainInterface for RelayChain { + async fn validators(&self, _: PHash) -> RelayChainResult> { + unimplemented!("Not needed for test") + } + + async fn best_block_hash(&self) -> RelayChainResult { + unimplemented!("Not needed for test") + } + + async fn finalized_block_hash(&self) -> RelayChainResult { + unimplemented!("Not needed for test") + } + + async fn retrieve_dmq_contents( + &self, + _: ParaId, + _: PHash, + ) -> RelayChainResult> { + let downward_msg = InboundDownwardMessage { + sent_at: 10u32, + msg: vec![1u8, 2u8, 3u8], + }; + Ok(vec![downward_msg]) + } + + async fn retrieve_all_inbound_hrmp_channel_contents( + &self, + _: ParaId, + _: PHash, + ) -> RelayChainResult>> { + let mut tree = BTreeMap::new(); + let hrmp_msg = InboundHrmpMessage { + sent_at: 10u32, + data: vec![1u8, 2u8, 3u8], + }; + let para_id = ParaId::from(2000u32); + tree.insert(para_id, vec![hrmp_msg]); + Ok(tree) + } + + async fn persisted_validation_data( + &self, + _hash: PHash, + _: ParaId, + _assumption: OccupiedCoreAssumption, + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } + + async fn candidate_pending_availability( + &self, + _: PHash, + _: ParaId, + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } + + async fn session_index_for_child(&self, _: PHash) -> RelayChainResult { + Ok(0) + } + + async fn import_notification_stream( + &self, + ) -> RelayChainResult + Send>>> { + unimplemented!("Not needed for test") + } + + async fn finality_notification_stream( + &self, + ) -> RelayChainResult + Send>>> { + unimplemented!("Not needed for test") + } + + async fn is_major_syncing(&self) -> RelayChainResult { + Ok(false) + } + + fn overseer_handle(&self) -> RelayChainResult { + unimplemented!("Not needed for test") + } + + async fn get_storage_by_key( + &self, + _: PHash, + _: &[u8], + ) -> RelayChainResult> { + Ok(None) + } + + async fn prove_read( + &self, + _: PHash, + _: &Vec>, + ) -> RelayChainResult { + let mut tree = BTreeSet::new(); + tree.insert(vec![1u8, 2u8, 3u8]); + let proof = sc_client_api::StorageProof::new(tree); + Ok(proof) + } + + async fn wait_for_block(&self, _: PHash) -> RelayChainResult<()> { + Ok(()) + } + + async fn new_best_notification_stream( + &self, + ) -> RelayChainResult + Send>>> { + unimplemented!("Not needed for test") + } + + async fn header(&self, _block_id: BlockId) -> RelayChainResult> { + unimplemented!("Not needed for test") + } +} + +#[derive(Clone)] +struct DummySpawner; +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + _name: &'static str, + _group: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + } + + fn spawn( + &self, + _name: &'static str, + _group: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + } +} + +struct DummyProposer(Arc); + +// This is going to be our block verifier +// It will mimic what the Nimbus verifier does, but again, Nimbus verifier is non-public +// It should substract the seal from logs and put it in post_logs +#[derive(Clone)] +pub struct SealExtractorVerfier { + finalized: bool, +} + +impl SealExtractorVerfier { + /// Create a new instance. + /// + /// Every verified block will use `finalized` for the `BlockImportParams`. + pub fn new(finalized: bool) -> Self { + Self { finalized } + } +} + +#[async_trait::async_trait] +impl sc_consensus::Verifier for SealExtractorVerfier { + async fn verify( + &mut self, + mut block: sc_consensus::BlockImportParams, + ) -> Result, String> { + if block.fork_choice.is_none() { + block.fork_choice = Some(ForkChoiceStrategy::LongestChain); + }; + //TODO: this could be done by making the nimbus verifier public (it is not) + + // Grab the seal digest. Assume it is last (since it is a seal after-all). + let seal = block + .header + .digest_mut() + .pop() + .ok_or("Block should have at least one digest on it")?; + + let signature = seal + .as_nimbus_seal() + .ok_or_else(|| String::from("HeaderUnsealed"))?; + + // Grab the author information from either the preruntime digest or the consensus digest + //TODO use the trait + let claimed_author = block + .header + .digest() + .logs + .iter() + .find_map(|digest| match *digest { + DigestItem::Consensus(id, ref author_id) if id == NIMBUS_ENGINE_ID => { + Some(author_id.clone()) + } + DigestItem::PreRuntime(id, ref author_id) if id == NIMBUS_ENGINE_ID => { + Some(author_id.clone()) + } + _ => None, + }) + .ok_or("Expected one consensus or pre-runtime digest that contains author id bytes")?; + + // Verify the signature + let valid_signature = NimbusPair::verify( + &signature, + block.header.hash(), + &NimbusId::from_slice(&claimed_author) + .map_err(|_| "Invalid Nimbus ID (wrong length)")?, + ); + + if !valid_signature { + return Err("Block signature invalid".into()); + } + block.post_digests.push(seal); + + block.finalized = self.finalized; + Ok(block) + } +} + +// The test Environment +impl Environment for DummyFactory { + type Proposer = DummyProposer; + type CreateProposer = future::Ready>; + type Error = Error; + + fn init(&mut self, _parent_header: &::Header) -> Self::CreateProposer { + future::ready(Ok(DummyProposer(self.0.clone()))) + } +} + +// how to propose the block by Dummy Proposer +impl Proposer for DummyProposer { + type Error = Error; + type Proposal = future::Ready, Error>>; + type ProofRecording = EnableProofRecording; + type Proof = sc_client_api::StorageProof; + + fn propose( + self, + _: InherentData, + digests: Digest, + _: Duration, + _: Option, + ) -> Self::Proposal { + let r = BlockBuilderBuilder::new(&*self.0) + .on_parent_block(self.0.chain_info().best_hash) + .fetch_parent_block_number(&*self.0) + .unwrap() + .with_inherent_digests(digests) + .build() + .unwrap() + .build(); + let (_relay_parent_storage_root, proof) = + RelayStateSproofBuilder::default().into_state_root_and_proof(); + + futures::future::ready(r.map(|b| Proposal { + block: b.block, + proof, + storage_changes: b.storage_changes, + })) + } +} + +type AuraPeer = Peer<(), PeersClient>; + +#[derive(Default)] +pub struct AuraTestNet { + peers: Vec, +} + +impl TestNetFactory for AuraTestNet { + type Verifier = SealExtractorVerfier; + type PeerData = (); + type BlockImport = PeersClient; + + fn make_block_import( + &self, + client: PeersClient, + ) -> ( + BlockImportAdapter, + Option>, + Self::PeerData, + ) { + ((client.as_block_import()), None, ()) + } + + fn make_verifier(&self, _client: PeersClient, _peer_data: &()) -> Self::Verifier { + SealExtractorVerfier::new(true) + } + + fn peer(&mut self, i: usize) -> &mut AuraPeer { + &mut self.peers[i] + } + + fn peers(&self) -> &Vec { + &self.peers + } + + fn peers_mut(&mut self) -> &mut Vec { + &mut self.peers + } + + fn mut_peers)>(&mut self, closure: F) { + closure(&mut self.peers); + } +} + +// Checks node slot claim. Again for different slots, different authorities +// should be able to claim +#[tokio::test] +async fn current_node_authority_should_claim_slot() { + let mut authorities: Vec = vec![ + Keyring::Alice.public().into(), + Keyring::Bob.public().into(), + Keyring::Charlie.public().into(), + ]; + + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."); + + let public = keystore + .sr25519_generate_new(NIMBUS_KEY_ID, None) + .expect("Key should be created"); + authorities.push(public.into()); + + let keystore_ptr: KeystorePtr = keystore.into(); + let mut claimed_slots = vec![]; + + for slot in 0..8 { + let dummy_head = TestHeader { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + }; + let aux_data = OrchestratorAuraWorkerAuxData { + authorities: authorities.clone(), + min_slot_freq: None, + }; + let claim = tanssi_claim_slot::( + aux_data, + &dummy_head, + slot.into(), + false, + &keystore_ptr, + ) + .unwrap(); + if claim.is_some() { + claimed_slots.push(slot); + } + } + + assert_eq!(claimed_slots, vec![3, 7]); +} + +#[tokio::test] +async fn claim_slot_respects_min_slot_freq() { + // There is only 1 authority, but it can only claim every 4 slots + let mut authorities: Vec = vec![]; + let min_slot_freq = 4; + + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."); + + let public = keystore + .sr25519_generate_new(NIMBUS_KEY_ID, None) + .expect("Key should be created"); + authorities.push(public.into()); + + let keystore_ptr: KeystorePtr = keystore.into(); + + let mut claimed_slots = vec![]; + + for slot in 0..10 { + let parent_slot: u64 = claimed_slots.last().copied().unwrap_or_default(); + let parent_slot: Slot = parent_slot.into(); + let pre_digest = Digest { + logs: vec![ + DigestItem::PreRuntime(AURA_ENGINE_ID, parent_slot.encode()), + //DigestItem::PreRuntime(NIMBUS_ENGINE_ID, authority.encode()), + ], + }; + let head = TestHeader { + parent_hash: Default::default(), + // If we use number=0 aura ignores the digest + number: claimed_slots.len() as u64, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: pre_digest, + }; + let aux_data = OrchestratorAuraWorkerAuxData { + authorities: authorities.clone(), + min_slot_freq: Some(min_slot_freq.into()), + }; + let claim = tanssi_claim_slot::( + aux_data, + &head, + slot.into(), + false, + &keystore_ptr, + ) + .unwrap(); + if claim.is_some() { + claimed_slots.push(slot); + } + } + + assert_eq!(claimed_slots, vec![0, 4, 8]); +} + +#[tokio::test] +async fn collate_returns_correct_block() { + let net = AuraTestNet::new(4); + + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."); + let alice_public = keystore + .sr25519_generate_new(NIMBUS_KEY_ID, Some(&Keyring::Alice.to_seed())) + .expect("Key should be created"); + + // Copy of the keystore needed for tanssi_claim_slot() + let keystore_copy = LocalKeystore::open(keystore_path.path(), None).expect("Copies keystore."); + keystore_copy + .sr25519_generate_new(NIMBUS_KEY_ID, Some(&Keyring::Alice.to_seed())) + .expect("Key should be copied"); + + let net = Arc::new(Mutex::new(net)); + + let mut net = net.lock(); + let peer = net.peer(3); + let client = peer.client().as_client(); + let environ = DummyFactory(client.clone()); + let spawner = DummySpawner; + let relay_client = RelayChain; + + // Build the collator + let mut collator = { + let params = CollatorParams { + create_inherent_data_providers: |_, _| async { + let slot = InherentDataProvider::from_timestamp_and_slot_duration( + Timestamp::current(), + SlotDuration::from_millis(SLOT_DURATION_MS), + ); + + Ok((slot,)) + }, + block_import: client.clone(), + relay_client: relay_client.clone(), + keystore: keystore.into(), + para_id: 1000.into(), + proposer: ConsensusProposer::new(environ.clone()), + collator_service: CollatorService::new( + client.clone(), + Arc::new(spawner), + Arc::new(move |_, _| {}), + Arc::new(environ), + ), + }; + + Collator::::new(params) + }; + + let mut head = client.expect_header(client.info().genesis_hash).unwrap(); + + // Modify the state root of the genesis header for it to match + // the one inside propose() function + let (relay_parent_storage_root, _proof) = + RelayStateSproofBuilder::default().into_state_root_and_proof(); + head.state_root = relay_parent_storage_root; + + // First we create inherent data + let (parachain_inherent_data, other_inherent_data) = collator + .create_inherent_data( + Default::default(), + &Default::default(), + head.clone().hash(), + None, + ) + .await + .unwrap(); + + // Params for tanssi_claim_slot() + let slot = InherentDataProvider::from_timestamp_and_slot_duration( + Timestamp::current(), + SlotDuration::from_millis(SLOT_DURATION_MS), + ); + let keystore_ptr: KeystorePtr = keystore_copy.into(); + + let mut claim = tanssi_claim_slot::( + OrchestratorAuraWorkerAuxData { + authorities: vec![alice_public.into()], + min_slot_freq: None, + }, + &head, + *slot, + false, + &keystore_ptr, + ) + .unwrap() + .unwrap(); + + // At the end we call collate() function + let res = collator + .collate( + &head, + &mut claim, + None, + (parachain_inherent_data, other_inherent_data), + Duration::from_millis(500), + 3_500_000usize, + ) + .await + .unwrap() + .unwrap() + .1; + + // The returned block should be imported and we should be able to get its header by now. + assert!(client.header(res.header().hash()).unwrap().is_some()); +} + +// Tests authorities are correctly returned and eligibility is correctly calculated +// thanks to the mocked runtime-apis +#[tokio::test] +async fn authorities_runtime_api_tests() { + let net = AuraTestNet::new(4); + let net = Arc::new(Mutex::new(net)); + + let mut net = net.lock(); + let peer = net.peer(3); + let client = peer.client().as_client(); + let environ = DummyFactory(client); + + let default_hash = Default::default(); + + let authorities = crate::authorities::<_, _, nimbus_primitives::NimbusPair>( + &environ, + &default_hash, + 1000u32.into(), + ); + + assert_eq!(authorities, Some(vec![Keyring::Alice.public().into()])); +} diff --git a/client/manual-xcm/Cargo.toml b/client/manual-xcm/Cargo.toml new file mode 100644 index 0000000..9bea98c --- /dev/null +++ b/client/manual-xcm/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "manual-xcm-rpc" +authors = { workspace = true } +edition = "2021" +license = "GPL-3.0-only" +repository = { workspace = true } +version = "0.1.0" + +[lints] +workspace = true + +[dependencies] +flume = { workspace = true } +futures = { workspace = true, features = [ "compat" ] } +hex-literal = { workspace = true } +jsonrpsee = { workspace = true, features = [ "macros", "server" ] } +parity-scale-codec = { workspace = true, features = [ "std" ] } +staging-xcm = { workspace = true } +tokio = { workspace = true, features = [ "sync", "time" ] } + +cumulus-primitives-core = { workspace = true, features = [ "std" ] } diff --git a/client/manual-xcm/src/lib.rs b/client/manual-xcm/src/lib.rs new file mode 100644 index 0000000..b112773 --- /dev/null +++ b/client/manual-xcm/src/lib.rs @@ -0,0 +1,157 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . +use { + cumulus_primitives_core::{ParaId, XcmpMessageFormat}, + jsonrpsee::{core::RpcResult, proc_macros::rpc}, + parity_scale_codec::Encode, + staging_xcm::{latest::prelude::*, opaque::lts::Weight}, +}; + +const DEFAULT_PROOF_SIZE: u64 = 64 * 1024; + +/// This RPC interface is used to manually submit XCM messages that will be injected into a +/// parachain-enabled runtime. This allows testing XCM logic in a controlled way in integration +/// tests. +#[rpc(server)] +#[jsonrpsee::core::async_trait] +pub trait ManualXcmApi { + /// Inject a downward xcm message - A message that comes from the relay chain. + /// You may provide an arbitrary message, or if you provide an emtpy byte array, + /// Then a default message (DOT transfer down to ALITH) will be injected + #[method(name = "xcm_injectDownwardMessage")] + async fn inject_downward_message(&self, message: Vec) -> RpcResult<()>; + + /// Inject an HRMP message - A message that comes from a dedicated channel to a sibling + /// parachain. + /// + /// Cumulus Parachain System seems to have a constraint that at most one hrmp message will be + /// sent on a channel per block. At least that's what this comment implies: + /// https://github.com/paritytech/cumulus/blob/c308c01b/pallets/parachain-system/src/lib.rs#L204 + /// Neither this RPC, nor the mock inherent data provider make any attempt to enforce this + /// constraint. In fact, violating it may be useful for testing. + /// The method accepts a sending paraId and a bytearray representing an arbitrary message as + /// parameters. If you provide an emtpy byte array, then a default message representing a + /// transfer of the sending paraId's native token will be injected. + #[method(name = "xcm_injectHrmpMessage")] + async fn inject_hrmp_message(&self, sender: ParaId, message: Vec) -> RpcResult<()>; +} + +pub struct ManualXcm { + pub downward_message_channel: flume::Sender>, + pub hrmp_message_channel: flume::Sender<(ParaId, Vec)>, +} + +#[jsonrpsee::core::async_trait] +impl ManualXcmApiServer for ManualXcm { + async fn inject_downward_message(&self, msg: Vec) -> RpcResult<()> { + let downward_message_channel = self.downward_message_channel.clone(); + // If no message is supplied, inject a default one. + let msg = if msg.is_empty() { + staging_xcm::VersionedXcm::<()>::V3(Xcm(vec![ + ReserveAssetDeposited((Parent, 10000000000000u128).into()), + ClearOrigin, + BuyExecution { + fees: (Parent, 10000000000000u128).into(), + weight_limit: Limited(Weight::from_parts( + 4_000_000_000u64, + DEFAULT_PROOF_SIZE * 2, + )), + }, + DepositAsset { + assets: AllCounted(1).into(), + beneficiary: MultiLocation::new( + 0, + X1(AccountKey20 { + network: None, + key: hex_literal::hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"), + }), + ), + }, + ])) + .encode() + } else { + msg + }; + + // Push the message to the shared channel where it will be queued up + // to be injected in to an upcoming block. + downward_message_channel + .send_async(msg) + .await + .map_err(|err| internal_err(err.to_string()))?; + + Ok(()) + } + + async fn inject_hrmp_message(&self, sender: ParaId, msg: Vec) -> RpcResult<()> { + let hrmp_message_channel = self.hrmp_message_channel.clone(); + + // If no message is supplied, inject a default one. + let msg = if msg.is_empty() { + let mut mes = XcmpMessageFormat::ConcatenatedVersionedXcm.encode(); + mes.append( + &mut (staging_xcm::VersionedXcm::<()>::V3(Xcm(vec![ + ReserveAssetDeposited( + ((Parent, Parachain(sender.into())), 10000000000000u128).into(), + ), + ClearOrigin, + BuyExecution { + fees: ((Parent, Parachain(sender.into())), 10000000000000u128).into(), + weight_limit: Limited(Weight::from_parts( + 4_000_000_000u64, + DEFAULT_PROOF_SIZE, + )), + }, + DepositAsset { + assets: AllCounted(1).into(), + beneficiary: MultiLocation::new( + 0, + X1(AccountKey20 { + network: None, + key: hex_literal::hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"), + }), + ), + }, + ])) + .encode()), + ); + mes + } else { + msg + }; + + // Push the message to the shared channel where it will be queued up + // to be injected in to an upcoming block. + hrmp_message_channel + .send_async((sender, msg)) + .await + .map_err(|err| internal_err(err.to_string()))?; + + Ok(()) + } +} + +// This bit cribbed from frontier. +pub fn internal_err>(message: T) -> jsonrpsee::core::Error { + jsonrpsee::core::Error::Call(jsonrpsee::types::error::CallError::Custom( + jsonrpsee::types::error::ErrorObject::borrowed( + jsonrpsee::types::error::INTERNAL_ERROR_CODE, + &message, + None, + ) + .into_owned(), + )) +} diff --git a/client/node-common/Cargo.toml b/client/node-common/Cargo.toml new file mode 100644 index 0000000..70aed71 --- /dev/null +++ b/client/node-common/Cargo.toml @@ -0,0 +1,90 @@ +[package] +name = "node-common" +authors = { workspace = true } +description = "Common code between various nodes" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[lints] +workspace = true + +[dependencies] +async-io = { workspace = true } +async-trait = { workspace = true } +clap = { workspace = true, features = [ "derive" ] } +core_extensions = { workspace = true, features = [ "type_identity" ] } +flume = { workspace = true } +futures = { workspace = true } +jsonrpsee = { workspace = true, features = [ "server" ] } +log = { workspace = true } +parity-scale-codec = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } + +# Local +tc-consensus = { workspace = true } + +# Nimbus +nimbus-consensus = { workspace = true } +nimbus-primitives = { workspace = true, features = [ "std" ] } + +# Substrate +frame-benchmarking = { workspace = true } +frame-benchmarking-cli = { workspace = true } +sc-basic-authorship = { workspace = true } +sc-chain-spec = { workspace = true } +sc-cli = { workspace = true } +sc-client-api = { workspace = true } +sc-consensus = { workspace = true } +sc-consensus-manual-seal = { workspace = true } +sc-consensus-slots = { workspace = true } +sc-executor = { workspace = true } +sc-network = { workspace = true } +sc-network-common = { workspace = true } +sc-network-sync = { workspace = true } +sc-network-transactions = { workspace = true } +sc-offchain = { workspace = true } +sc-rpc = { workspace = true } +sc-service = { workspace = true } +sc-sysinfo = { workspace = true } +sc-telemetry = { workspace = true } +sc-tracing = { workspace = true } +sc-transaction-pool = { workspace = true } +sc-transaction-pool-api = { workspace = true } +sc-utils = { workspace = true } +sp-api = { workspace = true, features = [ "std" ] } +sp-application-crypto = { workspace = true, features = [ "full_crypto", "std" ] } +sp-block-builder = { workspace = true } +sp-blockchain = { workspace = true } +sp-consensus = { workspace = true } +sp-consensus-aura = { workspace = true } +sp-core = { workspace = true, features = [ "std" ] } +sp-inherents = { workspace = true, features = [ "std" ] } +sp-io = { workspace = true, features = [ "std" ] } +sp-keystore = { workspace = true, features = [ "std" ] } +sp-offchain = { workspace = true, features = [ "std" ] } +sp-runtime = { workspace = true, features = [ "std" ] } +sp-session = { workspace = true, features = [ "std" ] } +sp-timestamp = { workspace = true, features = [ "std" ] } + +sp-transaction-pool = { workspace = true } +substrate-frame-rpc-system = { workspace = true } +substrate-prometheus-endpoint = { workspace = true } +try-runtime-cli = { workspace = true, optional = true } + +# Polkadot +polkadot-cli = { workspace = true } +polkadot-primitives = { workspace = true } +polkadot-service = { workspace = true } + +# Cumulus +cumulus-client-cli = { workspace = true } +cumulus-client-collator = { workspace = true } +cumulus-client-consensus-aura = { workspace = true } +cumulus-client-consensus-common = { workspace = true } +cumulus-client-consensus-proposer = { workspace = true } +cumulus-client-network = { workspace = true } +cumulus-client-service = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-parachain-inherent = { workspace = true } +cumulus-relay-chain-interface = { workspace = true } diff --git a/client/node-common/src/command.rs b/client/node-common/src/command.rs new file mode 100644 index 0000000..fda80d5 --- /dev/null +++ b/client/node-common/src/command.rs @@ -0,0 +1,60 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + parity_scale_codec::Encode, + sc_chain_spec::ChainSpec, + sp_runtime::{ + traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Zero}, + StateVersion, + }, +}; + +/// Generate the genesis block from a given ChainSpec. +pub fn generate_genesis_block( + chain_spec: &dyn ChainSpec, + genesis_state_version: StateVersion, +) -> Result { + let storage = chain_spec.build_storage()?; + + let child_roots = storage.children_default.iter().map(|(sk, child_content)| { + let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( + child_content.data.clone().into_iter().collect(), + genesis_state_version, + ); + (sk.clone(), state_root.encode()) + }); + let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( + storage.top.clone().into_iter().chain(child_roots).collect(), + genesis_state_version, + ); + + let extrinsics_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( + Vec::new(), + genesis_state_version, + ); + + Ok(Block::new( + <::Header as HeaderT>::new( + Zero::zero(), + extrinsics_root, + state_root, + Default::default(), + Default::default(), + ), + Default::default(), + )) +} diff --git a/client/node-common/src/lib.rs b/client/node-common/src/lib.rs new file mode 100644 index 0000000..36a704b --- /dev/null +++ b/client/node-common/src/lib.rs @@ -0,0 +1,19 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +pub mod service; + +pub mod command; diff --git a/client/node-common/src/service.rs b/client/node-common/src/service.rs new file mode 100644 index 0000000..4d599a1 --- /dev/null +++ b/client/node-common/src/service.rs @@ -0,0 +1,939 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + async_io::Timer, + core::time::Duration, + core_extensions::TypeIdentity, + cumulus_client_cli::CollatorOptions, + cumulus_client_consensus_common::ParachainConsensus, + cumulus_client_service::{ + build_relay_chain_interface, CollatorSybilResistance, StartFullNodeParams, + }, + cumulus_primitives_core::ParaId, + cumulus_relay_chain_interface::RelayChainInterface, + frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE, + futures::{channel::mpsc, FutureExt, Stream, StreamExt}, + jsonrpsee::RpcModule, + polkadot_primitives::CollatorPair, + sc_client_api::Backend, + sc_consensus::{import_queue::ImportQueueService, BlockImport, ImportQueue}, + sc_consensus_manual_seal::{ + run_manual_seal, ConsensusDataProvider, EngineCommand, ManualSealParams, + }, + sc_executor::{ + sp_wasm_interface::{ExtendedHostFunctions, HostFunctions}, + HeapAllocStrategy, NativeElseWasmExecutor, NativeExecutionDispatch, RuntimeVersionOf, + WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY, + }, + sc_network::{config::FullNetworkConfiguration, NetworkBlock, NetworkService}, + sc_network_sync::SyncingService, + sc_network_transactions::TransactionsHandlerController, + sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}, + sc_service::{ + Configuration, KeystoreContainer, NetworkStarter, SpawnTaskHandle, TFullBackend, + TFullClient, TaskManager, + }, + sc_telemetry::{Telemetry, TelemetryWorker, TelemetryWorkerHandle}, + sc_transaction_pool_api::OffchainTransactionPoolFactory, + sc_utils::mpsc::TracingUnboundedSender, + sp_api::ConstructRuntimeApi, + sp_block_builder::BlockBuilder, + sp_consensus::SelectChain, + sp_core::traits::CodeExecutor, + sp_inherents::CreateInherentDataProviders, + sp_offchain::OffchainWorkerApi, + sp_runtime::Percent, + sp_transaction_pool::runtime_api::TaggedTransactionQueue, + std::{str::FromStr, sync::Arc}, +}; + +/// Trait to configure the main types the builder rely on, bundled in a single +/// type to reduce verbosity and the amount of type parameters. +pub trait NodeBuilderConfig { + type Block; + type RuntimeApi; + type ParachainExecutor; + + /// Create a new `NodeBuilder` using the types of this `Config`, along + /// with the parachain `Configuration` and an optional `HwBench`. + fn new_builder( + parachain_config: &Configuration, + hwbench: Option, + ) -> Result, sc_service::Error> + where + Self: Sized, + BlockOf: cumulus_primitives_core::BlockT, + ExecutorOf: + Clone + CodeExecutor + RuntimeVersionOf + TanssiExecutorExt + Sync + Send + 'static, + RuntimeApiOf: + ConstructRuntimeApi, ClientOf> + Sync + Send + 'static, + ConstructedRuntimeApiOf: + TaggedTransactionQueue> + BlockBuilder>, + { + NodeBuilder::::new(parachain_config, hwbench) + } +} + +pub type BlockOf = ::Block; +pub type BlockHashOf = as cumulus_primitives_core::BlockT>::Hash; +pub type BlockHeaderOf = as cumulus_primitives_core::BlockT>::Header; +pub type RuntimeApiOf = ::RuntimeApi; +pub type ExecutorOf = ::ParachainExecutor; +pub type ClientOf = TFullClient, RuntimeApiOf, ExecutorOf>; +pub type BackendOf = TFullBackend>; +pub type ConstructedRuntimeApiOf = + as ConstructRuntimeApi, ClientOf>>::RuntimeApi; +pub type ImportQueueServiceOf = Box>>; +pub type ParachainConsensusOf = Box>>; + +// `Cumulus` and `TxHandler` are types that will change during the life of +// a `NodeBuilder` because they are generated and consumed when calling +// certain functions, with absence of data represented with `()`. Some +// function are implemented only for a given concrete type, which ensure it +// can only be called if the required data is available (generated and not yet +// consumed). +// +// While this could be implemented with multiple impl blocks with concrete types, +// we use here `core_extensions::TypeIdentity` which allow to express type +// identity/equality as a trait bound on each function as it removes the +// boilerplate of many impl block with duplicated trait bounds. 2 impl blocks +// are still required since Rust can't infer the types in the `new` function +// that doesn't take `self`. +pub struct NodeBuilder< + T: NodeBuilderConfig, + // `(cumulus_client_service/sc_service)::build_network` returns many important systems, + // but can only be called with an `import_queue` which can be different in + // each node. For that reason it is a `()` when calling `new`, then the + // caller create the `import_queue` using systems contained in `NodeBuilder`, + // then call `build_cumulus_network` with it to generate the cumulus systems. + SNetwork = (), + // The `TxHandler` is constructed in `build_X_network` + // and is then consumed when calling `spawn_common_tasks`. + STxHandler = (), + // The import queue service is obtained from the import queue in + // `build_cumulus_network` or `build_substrate_network`, which also + // consumes the import queue. Neither of them are clonable, so we need to + // to store the service here to be able to consume it later in + // `start_full_node`. + SImportQueueService = (), +> where + BlockOf: cumulus_primitives_core::BlockT, + ExecutorOf: Clone + CodeExecutor + RuntimeVersionOf + Sync + Send + 'static, + RuntimeApiOf: ConstructRuntimeApi, ClientOf> + Sync + Send + 'static, + ConstructedRuntimeApiOf: TaggedTransactionQueue> + BlockBuilder>, +{ + pub client: Arc>, + pub backend: Arc>, + pub task_manager: TaskManager, + pub keystore_container: KeystoreContainer, + pub transaction_pool: Arc, ClientOf>>, + pub telemetry: Option, + pub telemetry_worker_handle: Option, + + pub hwbench: Option, + pub prometheus_registry: Option, + + pub network: SNetwork, + pub tx_handler_controller: STxHandler, + pub import_queue_service: SImportQueueService, +} + +pub struct Network { + pub network: Arc>, + pub system_rpc_tx: TracingUnboundedSender>, + pub start_network: NetworkStarter, + pub sync_service: Arc>, +} + +/// Allows to create a parachain-defined executor from a `WasmExecutor` +pub trait TanssiExecutorExt { + type HostFun: HostFunctions; + fn new_with_wasm_executor(wasm_executor: WasmExecutor) -> Self; +} + +impl TanssiExecutorExt for WasmExecutor { + type HostFun = sp_io::SubstrateHostFunctions; + + fn new_with_wasm_executor(wasm_executor: WasmExecutor) -> Self { + wasm_executor + } +} + +impl TanssiExecutorExt for NativeElseWasmExecutor +where + D: NativeExecutionDispatch, +{ + type HostFun = ExtendedHostFunctions; + + fn new_with_wasm_executor(wasm_executor: WasmExecutor) -> Self { + NativeElseWasmExecutor::new_with_wasm_executor(wasm_executor) + } +} + +// `new` function doesn't take self, and the Rust compiler cannot infer that +// only one type T implements `TypeIdentity`. With thus need a separate impl +// block with concrete types `()`. +impl NodeBuilder +where + BlockOf: cumulus_primitives_core::BlockT, + ExecutorOf: + Clone + CodeExecutor + RuntimeVersionOf + TanssiExecutorExt + Sync + Send + 'static, + RuntimeApiOf: ConstructRuntimeApi, ClientOf> + Sync + Send + 'static, + ConstructedRuntimeApiOf: TaggedTransactionQueue> + BlockBuilder>, +{ + /// Create a new `NodeBuilder` which prepare objects required to launch a + /// node. However it only starts telemetry, and doesn't provide any + /// network-dependent objects (as it requires an import queue, which usually + /// is different for each node). + fn new( + parachain_config: &Configuration, + hwbench: Option, + ) -> Result { + // Refactor: old new_partial + + let telemetry = parachain_config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let heap_pages = + parachain_config + .default_heap_pages + .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { + extra_pages: h as u32, + }); + + // Default runtime_cache_size is 2 + // For now we can work with this, but it will likely need + // to change once we start having runtime_cache_sizes, or + // run nodes with the maximum for this value + let mut wasm_builder = WasmExecutor::builder() + .with_execution_method(parachain_config.wasm_method) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .with_max_runtime_instances(parachain_config.max_runtime_instances) + .with_runtime_cache_size(parachain_config.runtime_cache_size); + if let Some(ref wasmtime_precompiled_path) = parachain_config.wasmtime_precompiled { + wasm_builder = wasm_builder.with_wasmtime_precompiled_path(wasmtime_precompiled_path); + } + + let executor = ExecutorOf::::new_with_wasm_executor(wasm_builder.build()); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::, RuntimeApiOf, _>( + parachain_config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager + .spawn_handle() + .spawn("telemetry", None, worker.run()); + telemetry + }); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + parachain_config.transaction_pool.clone(), + parachain_config.role.is_authority().into(), + parachain_config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + Ok(Self { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry: parachain_config.prometheus_registry().cloned(), + network: TypeIdentity::from_type(()), + tx_handler_controller: TypeIdentity::from_type(()), + import_queue_service: TypeIdentity::from_type(()), + }) + } +} + +impl + NodeBuilder +where + BlockOf: cumulus_primitives_core::BlockT, + ExecutorOf: Clone + CodeExecutor + RuntimeVersionOf + Sync + Send + 'static, + RuntimeApiOf: ConstructRuntimeApi, ClientOf> + Sync + Send + 'static, + ConstructedRuntimeApiOf: TaggedTransactionQueue> + + BlockBuilder> + + cumulus_primitives_core::CollectCollationInfo>, +{ + pub async fn build_relay_chain_interface( + &mut self, + parachain_config: &Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + ) -> sc_service::error::Result<( + Arc<(dyn RelayChainInterface + 'static)>, + Option, + )> { + build_relay_chain_interface( + polkadot_config, + parachain_config, + self.telemetry_worker_handle.clone(), + &mut self.task_manager, + collator_options.clone(), + self.hwbench.clone(), + ) + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>)) + } + + /// Given an import queue, calls `cumulus_client_service::build_network` and + /// stores the returned objects in `self.network` and `self.tx_handler_controller`. + /// + /// Can only be called once on a `NodeBuilder` that doesn't have yet network + /// data. + pub async fn build_cumulus_network( + self, + parachain_config: &Configuration, + para_id: ParaId, + import_queue: impl ImportQueue> + 'static, + relay_chain_interface: RCInterface, + ) -> sc_service::error::Result< + NodeBuilder< + T, + Network>, + TransactionsHandlerController>, + ImportQueueServiceOf, + >, + > + where + SNetwork: TypeIdentity, + STxHandler: TypeIdentity, + SImportQueueService: TypeIdentity, + RCInterface: RelayChainInterface + Clone + 'static, + { + let Self { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network: _, + tx_handler_controller: _, + import_queue_service: _, + } = self; + + let net_config = FullNetworkConfiguration::new(¶chain_config.network); + let import_queue_service = import_queue.service(); + let spawn_handle = task_manager.spawn_handle(); + + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = + cumulus_client_service::build_network(cumulus_client_service::BuildNetworkParams { + parachain_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle, + import_queue, + para_id, + relay_chain_interface, + net_config, + sybil_resistance_level: CollatorSybilResistance::Resistant, + }) + .await?; + + Ok(NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network: Network { + network, + system_rpc_tx, + start_network, + sync_service, + }, + tx_handler_controller, + import_queue_service, + }) + } + + /// Given an import queue, calls `sc_service::build_network` and + /// stores the returned objects in `self.network` and `self.tx_handler_controller`. + /// + /// Can only be called once on a `NodeBuilder` that doesn't have yet network + /// data. + pub fn build_substrate_network( + self, + parachain_config: &Configuration, + import_queue: impl ImportQueue> + 'static, + ) -> sc_service::error::Result< + NodeBuilder< + T, + Network>, + TransactionsHandlerController>, + ImportQueueServiceOf, + >, + > + where + SNetwork: TypeIdentity, + STxHandler: TypeIdentity, + SImportQueueService: TypeIdentity, + { + let Self { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network: _, + tx_handler_controller: _, + import_queue_service: _, + } = self; + + let net_config = FullNetworkConfiguration::new(¶chain_config.network); + let import_queue_service = import_queue.service(); + + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: parachain_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue, + warp_sync_params: None, + block_announce_validator_builder: None, + net_config, + block_relay: None, + })?; + + Ok(NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network: Network { + network, + system_rpc_tx, + start_network, + sync_service, + }, + tx_handler_controller, + import_queue_service, + }) + } + + /// Given an `rpc_builder`, spawns the common tasks of a Substrate + /// node. It consumes `self.tx_handler_controller` in the process, which means + /// it can only be called once, and any other code that would need this + /// controller should interact with it before calling this function. + pub fn spawn_common_tasks( + self, + parachain_config: Configuration, + rpc_builder: Box< + dyn Fn( + DenyUnsafe, + SubscriptionTaskExecutor, + ) -> Result, sc_service::Error>, + >, + ) -> sc_service::error::Result>, (), SImportQueueService>> + where + SNetwork: TypeIdentity>>, + STxHandler: TypeIdentity>>, + BlockHashOf: Unpin, + BlockHeaderOf: Unpin, + ConstructedRuntimeApiOf: TaggedTransactionQueue> + + BlockBuilder> + + OffchainWorkerApi> + + sp_api::Metadata> + + sp_session::SessionKeys>, + { + let NodeBuilder { + client, + backend, + transaction_pool, + mut telemetry, + telemetry_worker_handle, + mut task_manager, + keystore_container, + hwbench, + prometheus_registry, + network, + tx_handler_controller, + import_queue_service, + } = self; + + let network = TypeIdentity::into_type(network); + let tx_handler_controller = TypeIdentity::into_type(tx_handler_controller); + + let collator = parachain_config.role.is_authority(); + + if parachain_config.offchain_worker.enabled { + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { + runtime_api_provider: client.clone(), + keystore: Some(keystore_container.keystore()), + offchain_db: backend.offchain_storage(), + transaction_pool: Some(OffchainTransactionPoolFactory::new( + transaction_pool.clone(), + )), + network_provider: network.network.clone(), + is_validator: parachain_config.role.is_authority(), + enable_http_requests: false, + custom_extensions: move |_| vec![], + }) + .run(client.clone(), task_manager.spawn_handle()) + .boxed(), + ); + } + + let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + rpc_builder, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: keystore_container.keystore(), + backend: backend.clone(), + network: network.network.clone(), + system_rpc_tx: network.system_rpc_tx.clone(), + tx_handler_controller, + telemetry: telemetry.as_mut(), + sync_service: network.sync_service.clone(), + })?; + + if let Some(hwbench) = &hwbench { + sc_sysinfo::print_hwbench(hwbench); + // Here you can check whether the hardware meets your chains' requirements. Putting a link + // in there and swapping out the requirements for your own are probably a good idea. The + // requirements for a para-chain are dictated by its relay-chain. + if collator { + if let Err(err) = SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.", + err + ); + } + } + + if let Some(ref mut telemetry) = telemetry { + let telemetry_handle = telemetry.handle(); + task_manager.spawn_handle().spawn( + "telemetry_hwbench", + None, + sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench.clone()), + ); + } + } + + Ok(NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network: TypeIdentity::from_type(network), + tx_handler_controller: TypeIdentity::from_type(()), + import_queue_service, + }) + } + + pub fn install_manual_seal( + &mut self, + manual_seal_config: ManualSealConfiguration, BI, SC, CIDP>, + ) -> sc_service::error::Result>>>> + where + BI: BlockImport, Error = sp_consensus::Error> + Send + Sync + 'static, + SC: SelectChain> + 'static, + CIDP: CreateInherentDataProviders, ()> + 'static, + { + let ManualSealConfiguration { + sealing, + soft_deadline, + block_import, + select_chain, + consensus_data_provider, + create_inherent_data_providers, + } = manual_seal_config; + + let prometheus_registry = self.prometheus_registry.clone(); + + let mut env = sc_basic_authorship::ProposerFactory::new( + self.task_manager.spawn_handle(), + self.client.clone(), + self.transaction_pool.clone(), + prometheus_registry.as_ref(), + self.telemetry.as_ref().map(|x| x.handle()), + ); + + let mut command_sink = None; + + if let Some(deadline) = soft_deadline { + env.set_soft_deadline(deadline); + } + + let commands_stream: Box< + dyn Stream>> + Send + Sync + Unpin, + > = match sealing { + Sealing::Instant => { + Box::new( + // This bit cribbed from the implementation of instant seal. + self.transaction_pool + .pool() + .validated_pool() + .import_notification_stream() + .map(|_| EngineCommand::SealNewBlock { + create_empty: false, + finalize: false, + parent_hash: None, + sender: None, + }), + ) + } + Sealing::Manual => { + let (sink, stream) = futures::channel::mpsc::channel(1000); + // Keep a reference to the other end of the channel. It goes to the RPC. + command_sink = Some(sink); + Box::new(stream) + } + Sealing::Interval(millis) => Box::new(futures::StreamExt::map( + Timer::interval(Duration::from_millis(millis)), + |_| EngineCommand::SealNewBlock { + create_empty: true, + finalize: true, + parent_hash: None, + sender: None, + }, + )), + }; + + self.task_manager.spawn_essential_handle().spawn_blocking( + "authorship_task", + Some("block-authoring"), + run_manual_seal(ManualSealParams { + block_import, + env, + client: self.client.clone(), + pool: self.transaction_pool.clone(), + commands_stream, + select_chain, + consensus_data_provider, + create_inherent_data_providers, + }), + ); + + Ok(command_sink) + } + + pub fn start_full_node( + self, + para_id: ParaId, + relay_chain_interface: RCInterface, + relay_chain_slot_duration: Duration, + ) -> sc_service::error::Result> + where + SNetwork: TypeIdentity>>, + SImportQueueService: TypeIdentity>, + RCInterface: RelayChainInterface + Clone + 'static, + { + let NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + mut task_manager, + keystore_container, + hwbench, + prometheus_registry, + network, + tx_handler_controller, + import_queue_service, + } = self; + + let network = TypeIdentity::into_type(network); + let import_queue_service = TypeIdentity::into_type(import_queue_service); + + let announce_block = { + let sync_service = network.sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + let params = StartFullNodeParams { + client: client.clone(), + announce_block, + task_manager: &mut task_manager, + para_id, + relay_chain_interface, + relay_chain_slot_duration, + import_queue: import_queue_service, + recovery_handle: Box::new(overseer_handle), + sync_service: network.sync_service.clone(), + }; + + // TODO: change for async backing + #[allow(deprecated)] + cumulus_client_service::start_full_node(params)?; + + Ok(NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network: TypeIdentity::from_type(network), + tx_handler_controller, + import_queue_service: (), + }) + } + + pub async fn start_collator( + self, + para_id: ParaId, + relay_chain_interface: RCInterface, + relay_chain_slot_duration: Duration, + parachain_consensus: ParachainConsensusOf, + collator_key: CollatorPair, + ) -> sc_service::error::Result> + where + SNetwork: TypeIdentity>>, + SImportQueueService: TypeIdentity>, + RCInterface: RelayChainInterface + Clone + 'static, + { + let NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + mut task_manager, + keystore_container, + hwbench, + prometheus_registry, + network, + tx_handler_controller, + import_queue_service, + } = self; + + let network = TypeIdentity::into_type(network); + let import_queue_service = TypeIdentity::into_type(import_queue_service); + + let spawner = task_manager.spawn_handle(); + let announce_block = { + let sync_service = network.sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + let params = cumulus_client_service::StartCollatorParams { + para_id, + block_status: client.clone(), + announce_block: announce_block.clone(), + client: client.clone(), + task_manager: &mut task_manager, + relay_chain_interface: relay_chain_interface.clone(), + spawner: spawner.clone(), + parachain_consensus, + import_queue: import_queue_service, + collator_key, + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle.clone()), + sync_service: network.sync_service.clone(), + }; + + // TODO: change for async backing + #[allow(deprecated)] + cumulus_client_service::start_collator(params).await?; + + Ok(NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network: TypeIdentity::from_type(network), + tx_handler_controller, + import_queue_service: (), + }) + } + + pub fn extract_import_queue_service( + self, + ) -> ( + NodeBuilder, + SImportQueueService, + ) + where + SNetwork: TypeIdentity>>, + { + let NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network, + tx_handler_controller, + import_queue_service, + } = self; + + ( + NodeBuilder { + client, + backend, + transaction_pool, + telemetry, + telemetry_worker_handle, + task_manager, + keystore_container, + hwbench, + prometheus_registry, + network, + tx_handler_controller, + import_queue_service: (), + }, + import_queue_service, + ) + } + + pub fn cumulus_client_collator_params_generator( + &self, + para_id: ParaId, + overseer_handle: cumulus_relay_chain_interface::OverseerHandle, + collator_key: CollatorPair, + parachain_consensus: ParachainConsensusOf, + ) -> impl Fn() -> cumulus_client_collator::StartCollatorParams< + BlockOf, + ClientOf, + ClientOf, + SpawnTaskHandle, + > + Send + + Clone + + 'static + where + SNetwork: TypeIdentity>>, + { + let network = TypeIdentity::as_type(&self.network); + + let client = self.client.clone(); + let announce_block = { + let sync_service = network.sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + let spawner = self.task_manager.spawn_handle(); + + move || cumulus_client_collator::StartCollatorParams { + runtime_api: client.clone(), + block_status: client.clone(), + announce_block: announce_block.clone(), + overseer_handle: overseer_handle.clone(), + spawner: spawner.clone(), + para_id, + key: collator_key.clone(), + parachain_consensus: parachain_consensus.clone(), + } + } +} + +/// Block authoring scheme to be used by the dev service. +#[derive(Debug, Copy, Clone)] +pub enum Sealing { + /// Author a block immediately upon receiving a transaction into the transaction pool + Instant, + /// Author a block upon receiving an RPC command + Manual, + /// Author blocks at a regular interval specified in milliseconds + Interval(u64), +} + +impl FromStr for Sealing { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "instant" => Self::Instant, + "manual" => Self::Manual, + s => { + let millis = s + .parse::() + .map_err(|_| "couldn't decode sealing param")?; + Self::Interval(millis) + } + }) + } +} + +pub struct ManualSealConfiguration { + pub sealing: Sealing, + pub block_import: BI, + pub soft_deadline: Option, + pub select_chain: SC, + pub consensus_data_provider: Option>>, + pub create_inherent_data_providers: CIDP, +} diff --git a/client/services-payment/Cargo.toml b/client/services-payment/Cargo.toml new file mode 100644 index 0000000..f646814 --- /dev/null +++ b/client/services-payment/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "services-payment-rpc" +authors = { workspace = true } +description = "RPC interface for the Services Payment pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +futures = { workspace = true } +jsonrpsee = { workspace = true } +pallet-services-payment-runtime-api = { workspace = true, features = [ "std" ] } +parity-scale-codec = { workspace = true } +sc-client-api = { workspace = true } +sp-api = { workspace = true, features = [ "std" ] } +sp-runtime = { workspace = true, features = [ "std" ] } diff --git a/client/services-payment/src/lib.rs b/client/services-payment/src/lib.rs new file mode 100644 index 0000000..7ff984d --- /dev/null +++ b/client/services-payment/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! RPC client for Services Payment pallet + +pub use pallet_services_payment_runtime_api::ServicesPaymentApi as ServicesPaymentRuntimeApi; +use { + core::marker::PhantomData, + jsonrpsee::{ + core::{async_trait, RpcResult}, + proc_macros::rpc, + }, + sc_client_api::UsageProvider, + sp_api::ProvideRuntimeApi, + sp_runtime::traits::Block as BlockT, + std::sync::Arc, +}; + +#[rpc(server)] +pub trait ServicesPaymentApi { + #[method(name = "tanssi_servicesPaymentBlockCost")] + async fn block_cost(&self, para_id: ParaId) -> RpcResult; + + #[method(name = "tanssi_servicesPaymentCollatorAssignmentCost")] + async fn collator_assignment_cost(&self, para_id: ParaId) -> RpcResult; +} + +pub struct ServicesPayment { + client: Arc, + _phantom: PhantomData, +} + +impl ServicesPayment { + pub fn new(client: Arc) -> Self { + Self { + client, + _phantom: PhantomData, + } + } +} + +#[async_trait] +impl ServicesPaymentApiServer + for ServicesPayment +where + Hash: Send + 'static, + Block: BlockT, + Client: ProvideRuntimeApi + Sync + Send + UsageProvider + 'static, + Client::Api: ServicesPaymentRuntimeApi, + Balance: parity_scale_codec::Codec + Send + 'static, + ParaId: parity_scale_codec::Codec + Send + 'static, +{ + async fn block_cost(&self, para_id: ParaId) -> RpcResult { + let cost = self + .client + .runtime_api() + .block_cost(self.client.usage_info().chain.best_hash, para_id) + .map_err(|e| internal_err(e))?; + Ok(cost) + } + + async fn collator_assignment_cost(&self, para_id: ParaId) -> RpcResult { + let cost = self + .client + .runtime_api() + .collator_assignment_cost(self.client.usage_info().chain.best_hash, para_id) + .map_err(|e| internal_err(e))?; + Ok(cost) + } +} + +pub fn internal_err(error: T) -> jsonrpsee::core::Error { + jsonrpsee::core::Error::Call(jsonrpsee::types::error::CallError::Custom( + jsonrpsee::types::error::ErrorObject::borrowed( + jsonrpsee::types::error::INTERNAL_ERROR_CODE, + &error.to_string(), + None, + ) + .into_owned(), + )) +} diff --git a/client/stream-payment/Cargo.toml b/client/stream-payment/Cargo.toml new file mode 100644 index 0000000..49f5816 --- /dev/null +++ b/client/stream-payment/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "stream-payment-rpc" +authors = { workspace = true } +description = "RPC interface for the Stream Payment pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +futures = { workspace = true } +jsonrpsee = { workspace = true } +pallet-stream-payment-runtime-api = { workspace = true, features = [ "std" ] } +parity-scale-codec = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } +sp-api = { workspace = true, features = [ "std" ] } +sp-runtime = { workspace = true, features = [ "std" ] } +thiserror = { workspace = true } diff --git a/client/stream-payment/src/lib.rs b/client/stream-payment/src/lib.rs new file mode 100644 index 0000000..86b9b51 --- /dev/null +++ b/client/stream-payment/src/lib.rs @@ -0,0 +1,107 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! RPC client for Author Noting pallet + +pub use pallet_stream_payment_runtime_api::StreamPaymentApi as StreamPaymentRuntimeApi; +use { + core::marker::PhantomData, + jsonrpsee::{ + core::{async_trait, RpcResult}, + proc_macros::rpc, + }, + pallet_stream_payment_runtime_api::{StreamPaymentApiError, StreamPaymentApiStatus}, + sp_api::ProvideRuntimeApi, + sp_runtime::traits::Block as BlockT, + std::sync::Arc, +}; + +/// Top-level error type for the RPC handler. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Failed to fetch API + #[error("Failed to fetch API: {0}")] + ApiError(sp_api::ApiError), + + /// Failed to fetch the current best header. + #[error("Failed to fetch stream payment status: {0}")] + StreamPaymentApiError(StreamPaymentApiError), +} + +#[rpc(client, server)] +pub trait StreamPaymentApi { + #[method(name = "tanssi_streamPaymentStatus")] + async fn stream_payment_status( + &self, + block: Hash, + stream_id: StreamId, + now: Option, + ) -> RpcResult>; +} + +pub struct StreamPayment { + client: Arc, + _phantom: PhantomData, +} + +impl StreamPayment { + pub fn new(client: Arc) -> Self { + Self { + client, + _phantom: PhantomData, + } + } +} + +#[async_trait] +impl + StreamPaymentApiServer for StreamPayment +where + Hash: Send + 'static, + Block: BlockT, + Client: ProvideRuntimeApi + Sync + Send + 'static, + Client::Api: StreamPaymentRuntimeApi, + StreamId: parity_scale_codec::Codec + Send + 'static, + Instant: parity_scale_codec::Codec + Send + 'static, + Balance: parity_scale_codec::Codec + Send + 'static, +{ + async fn stream_payment_status( + &self, + block: Hash, + stream_id: StreamId, + now: Option, + ) -> RpcResult> { + let status = self + .client + .runtime_api() + .stream_payment_status(block, stream_id, now) + .map_err(|e| internal_err(Error::ApiError(e)))? + .map_err(|e| internal_err(Error::StreamPaymentApiError(e)))?; + + Ok(status) + } +} + +pub fn internal_err(error: T) -> jsonrpsee::core::Error { + jsonrpsee::core::Error::Call(jsonrpsee::types::error::CallError::Custom( + jsonrpsee::types::error::ErrorObject::borrowed( + jsonrpsee::types::error::INTERNAL_ERROR_CODE, + &error.to_string(), + None, + ) + .into_owned(), + )) +} diff --git a/container-chains/nodes/frontier/Cargo.toml b/container-chains/nodes/frontier/Cargo.toml new file mode 100644 index 0000000..ef5b46e --- /dev/null +++ b/container-chains/nodes/frontier/Cargo.toml @@ -0,0 +1,140 @@ +[package] +name = "container-chain-frontier-node" +authors = { workspace = true } +build = "build.rs" +description = "Frontier container chain template node" +edition = "2021" +license = "GPL-3.0-only" +version = "0.7.0" + +[lints] +workspace = true + +[dependencies] +async-io = { workspace = true } +async-trait = { workspace = true } +clap = { workspace = true, features = [ "derive" ] } +flume = { workspace = true } +futures = { workspace = true } +hex-literal = { workspace = true } +jsonrpsee = { workspace = true, features = [ "server" ] } +log = { workspace = true } +parity-scale-codec = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } +serde_json = { workspace = true, features = [ "arbitrary_precision" ] } +url = { workspace = true } + +# Local +ccp-authorities-noting-inherent = { workspace = true } +container-chain-template-frontier-runtime = { workspace = true, features = [ "std" ] } +manual-xcm-rpc = { workspace = true } +node-common = { workspace = true } +tc-consensus = { workspace = true } + +# Nimbus +nimbus-consensus = { workspace = true } +nimbus-primitives = { workspace = true, features = [ "std" ] } + +# Substrate +frame-benchmarking = { workspace = true } +frame-benchmarking-cli = { workspace = true } +frame-system-rpc-runtime-api = { workspace = true } +pallet-transaction-payment-rpc = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true, features = [ "std" ] } +sc-basic-authorship = { workspace = true } +sc-chain-spec = { workspace = true } +sc-cli = { workspace = true } +sc-client-api = { workspace = true } +sc-consensus = { workspace = true } +sc-consensus-manual-seal = { workspace = true } +sc-executor = { workspace = true } +sc-network = { workspace = true } +sc-network-common = { workspace = true } +sc-network-sync = { workspace = true } +sc-offchain = { workspace = true } +sc-rpc = { workspace = true } +sc-service = { workspace = true } +sc-sysinfo = { workspace = true } +sc-telemetry = { workspace = true } +sc-tracing = { workspace = true } +sc-transaction-pool = { workspace = true } +sc-transaction-pool-api = { workspace = true } +sp-api = { workspace = true, features = [ "std" ] } +sp-block-builder = { workspace = true } +sp-blockchain = { workspace = true } +sp-consensus = { workspace = true } +sp-debug-derive = { workspace = true } + +sp-consensus-aura = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true, features = [ "std" ] } +sp-inherents = { workspace = true, features = [ "std" ] } +sp-io = { workspace = true, features = [ "std" ] } +sp-keystore = { workspace = true, features = [ "std" ] } +sp-offchain = { workspace = true, features = [ "std" ] } +sp-runtime = { workspace = true, features = [ "std" ] } +sp-session = { workspace = true, features = [ "std" ] } +sp-timestamp = { workspace = true, features = [ "std" ] } + +sp-transaction-pool = { workspace = true } +substrate-frame-rpc-system = { workspace = true } +substrate-prometheus-endpoint = { workspace = true } +try-runtime-cli = { workspace = true, optional = true } + +# Polkadot +polkadot-cli = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-primitives = { workspace = true } +polkadot-service = { workspace = true } + +# Cumulus +cumulus-client-cli = { workspace = true } +cumulus-client-consensus-aura = { workspace = true } +cumulus-client-consensus-common = { workspace = true } +cumulus-client-network = { workspace = true } +cumulus-client-parachain-inherent = { workspace = true } +cumulus-client-service = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-relay-chain-interface = { workspace = true } +cumulus-test-relay-sproof-builder = { workspace = true } + +# Frontier +fc-api = { workspace = true } +fc-cli = { workspace = true } +fc-consensus = { workspace = true } +fc-db = { workspace = true, features = [ "sql" ] } +fc-mapping-sync = { workspace = true, features = [ "sql" ] } +fc-rpc = { workspace = true, features = [ "txpool" ] } +fc-rpc-core = { workspace = true, features = [ "txpool" ] } +fc-storage = { workspace = true } +fp-evm = { workspace = true } +fp-rpc = { workspace = true } +pallet-ethereum = { workspace = true } +[build-dependencies] +substrate-build-script-utils = { workspace = true } + +[features] +default = [] +runtime-benchmarks = [ + "container-chain-template-frontier-runtime/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking-cli/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "nimbus-primitives/runtime-benchmarks", + "pallet-ethereum/runtime-benchmarks", + "polkadot-cli/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-service/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "container-chain-template-frontier-runtime/try-runtime", + "nimbus-primitives/try-runtime", + "pallet-ethereum/try-runtime", + "polkadot-cli/try-runtime", + "polkadot-service/try-runtime", + "sp-runtime/try-runtime", + "try-runtime-cli/try-runtime", +] diff --git a/container-chains/nodes/frontier/build.rs b/container-chains/nodes/frontier/build.rs new file mode 100644 index 0000000..cbaa443 --- /dev/null +++ b/container-chains/nodes/frontier/build.rs @@ -0,0 +1,23 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/container-chains/nodes/frontier/src/chain_spec.rs b/container-chains/nodes/frontier/src/chain_spec.rs new file mode 100644 index 0000000..c6c158f --- /dev/null +++ b/container-chains/nodes/frontier/src/chain_spec.rs @@ -0,0 +1,224 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + container_chain_template_frontier_runtime::{ + AccountId, EVMChainIdConfig, EVMConfig, MaintenanceModeConfig, MigrationsConfig, + PolkadotXcmConfig, Precompiles, + }, + cumulus_primitives_core::ParaId, + fp_evm::GenesisAccount, + hex_literal::hex, + sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}, + sc_network::config::MultiaddrWithPeerId, + sc_service::ChainType, + serde::{Deserialize, Serialize}, +}; + +/// Specialized `ChainSpec` for the normal parachain runtime. +pub type ChainSpec = sc_service::GenericChainSpec< + container_chain_template_frontier_runtime::RuntimeGenesisConfig, + Extensions, +>; + +/// Orcherstrator's parachain id +pub const ORCHESTRATOR: ParaId = ParaId::new(1000); + +/// The extensions for the [`ChainSpec`]. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] +#[serde(deny_unknown_fields)] +pub struct Extensions { + /// The relay chain of the Parachain. + pub relay_chain: String, + /// The id of the Parachain. + pub para_id: u32, +} + +impl Extensions { + /// Try to get the extension from the given `ChainSpec`. + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +pub fn development_config(para_id: ParaId, boot_nodes: Vec) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 18.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), true.into()); + + let mut default_funded_accounts = pre_funded_accounts(); + default_funded_accounts.sort(); + default_funded_accounts.dedup(); + let boot_nodes: Vec = boot_nodes + .into_iter() + .map(|x| { + x.parse::() + .unwrap_or_else(|e| panic!("invalid bootnode address format {:?}: {:?}", x, e)) + }) + .collect(); + + ChainSpec::builder( + container_chain_template_frontier_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name("Development") + .with_id("dev") + .with_chain_type(ChainType::Development) + .with_genesis_config(testnet_genesis( + default_funded_accounts.clone(), + para_id, + AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), // Alith + )) + .with_properties(properties) + .with_boot_nodes(boot_nodes) + .build() +} + +pub fn local_testnet_config(para_id: ParaId, boot_nodes: Vec) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 18.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), true.into()); + let protocol_id = format!("container-chain-{}", para_id); + + let mut default_funded_accounts = pre_funded_accounts(); + default_funded_accounts.sort(); + default_funded_accounts.dedup(); + let boot_nodes: Vec = boot_nodes + .into_iter() + .map(|x| { + x.parse::() + .unwrap_or_else(|e| panic!("invalid bootnode address format {:?}: {:?}", x, e)) + }) + .collect(); + + ChainSpec::builder( + container_chain_template_frontier_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name(&format!("Frontier Container {}", para_id)) + .with_id(&format!("frontier_container_{}", para_id)) + .with_chain_type(ChainType::Local) + .with_genesis_config(testnet_genesis( + default_funded_accounts.clone(), + para_id, + AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), // Alith + )) + .with_properties(properties) + .with_protocol_id(&protocol_id) + .with_boot_nodes(boot_nodes) + .build() +} + +fn testnet_genesis( + endowed_accounts: Vec, + id: ParaId, + root_key: AccountId, +) -> serde_json::Value { + // This is the simplest bytecode to revert without returning any data. + // We will pre-deploy it under all of our precompiles to ensure they can be called from + // within contracts. + // (PUSH1 0x00 PUSH1 0x00 REVERT) + let revert_bytecode = vec![0x60, 0x00, 0x60, 0x00, 0xFD]; + + let g = container_chain_template_frontier_runtime::RuntimeGenesisConfig { + system: Default::default(), + balances: container_chain_template_frontier_runtime::BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1 << 80)) + .collect(), + }, + parachain_info: container_chain_template_frontier_runtime::ParachainInfoConfig { + parachain_id: id, + ..Default::default() + }, + parachain_system: Default::default(), + // EVM compatibility + // We should change this to something different than Moonbeam + // For now moonwall is very tailored for moonbeam so we need it for tests + evm_chain_id: EVMChainIdConfig { + chain_id: 1281, + ..Default::default() + }, + evm: EVMConfig { + // We need _some_ code inserted at the precompile address so that + // the evm will actually call the address. + accounts: Precompiles::used_addresses() + .map(|addr| { + ( + addr.into(), + GenesisAccount { + nonce: Default::default(), + balance: Default::default(), + storage: Default::default(), + code: revert_bytecode.clone(), + }, + ) + }) + .collect(), + ..Default::default() + }, + ethereum: Default::default(), + base_fee: Default::default(), + transaction_payment: Default::default(), + sudo: container_chain_template_frontier_runtime::SudoConfig { + key: Some(root_key), + }, + authorities_noting: container_chain_template_frontier_runtime::AuthoritiesNotingConfig { + orchestrator_para_id: ORCHESTRATOR, + ..Default::default() + }, + migrations: MigrationsConfig { + ..Default::default() + }, + maintenance_mode: MaintenanceModeConfig { + start_in_maintenance_mode: false, + ..Default::default() + }, + // This should initialize it to whatever we have set in the pallet + polkadot_xcm: PolkadotXcmConfig::default(), + tx_pause: Default::default(), + }; + + serde_json::to_value(g).unwrap() +} + +/// Get pre-funded accounts +pub fn pre_funded_accounts() -> Vec { + // These addresses are derived from Substrate's canonical mnemonic: + // bottom drive obey lake curtain smoke basket hold race lonely fit walk + vec![ + AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), // Alith + AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")), // Baltathar + AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")), // Charleth + AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")), // Dorothy + ] +} diff --git a/container-chains/nodes/frontier/src/cli.rs b/container-chains/nodes/frontier/src/cli.rs new file mode 100644 index 0000000..891f18c --- /dev/null +++ b/container-chains/nodes/frontier/src/cli.rs @@ -0,0 +1,207 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + clap::Parser, + node_common::service::Sealing, + sc_cli::{CliConfiguration, NodeKeyParams, SharedParams}, + std::path::PathBuf, +}; + +/// Sub-commands supported by the collator. +#[derive(Debug, clap::Subcommand)] +#[allow(clippy::large_enum_variant)] +pub enum Subcommand { + /// Build a chain specification. + BuildSpec(BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Remove the whole chain. + PurgeChain(cumulus_client_cli::PurgeChainCmd), + + /// Export the genesis state of the parachain. + #[command(alias = "export-genesis-state")] + ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand), + + /// Export the genesis wasm of the parachain. + ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand), + + /// Sub-commands concerned with benchmarking. + /// The pallet benchmarking moved to the `pallet` sub-command. + #[command(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), + + /// Try some testing command against a specified runtime state. + #[cfg(feature = "try-runtime")] + TryRuntime(try_runtime_cli::TryRuntimeCmd), + + /// Errors since the binary was not build with `--features try-runtime`. + #[cfg(not(feature = "try-runtime"))] + TryRuntime, + + /// Precompile the WASM runtime into native code + PrecompileWasm(sc_cli::PrecompileWasmCmd), +} + +#[derive(Debug, Parser)] +#[group(skip)] +pub struct RunCmd { + #[clap(flatten)] + pub base: cumulus_client_cli::RunCmd, + + /// Size in bytes of the LRU cache for block data. + #[arg(long, default_value = "300000000")] + pub eth_log_block_cache: usize, + + /// Size in bytes of the LRU cache for transactions statuses data. + #[arg(long, default_value = "300000000")] + pub eth_statuses_cache: usize, + + /// Maximum number of logs in a query. + #[arg(long, default_value = "10000")] + pub max_past_logs: u32, + + /// Id of the parachain this collator collates for. + #[arg(long)] + pub parachain_id: Option, + + /// Maximum fee history cache size. + #[arg(long, default_value = "2048")] + pub fee_history_limit: u64, + + /// When blocks should be sealed in the dev service. + /// + /// Options are "instant", "manual", or timer interval in milliseconds + #[arg(long, default_value = "instant")] + pub sealing: Sealing, +} + +impl std::ops::Deref for RunCmd { + type Target = cumulus_client_cli::RunCmd; + + fn deref(&self) -> &Self::Target { + &self.base + } +} + +#[derive(Debug, clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[command(subcommand)] + pub subcommand: Option, + + #[command(flatten)] + pub run: RunCmd, + + /// Disable automatic hardware benchmarks. + /// + /// By default these benchmarks are automatically ran at startup and measure + /// the CPU speed, the memory bandwidth and the disk speed. + /// + /// The results are then printed out in the logs, and also sent as part of + /// telemetry, if telemetry is enabled. + #[arg(long)] + pub no_hardware_benchmarks: bool, + + /// Relay chain arguments + #[arg(raw = true)] + pub relay_chain_args: Vec, + + /// Optional parachain id that should be used to build chain spec. + #[arg(long)] + pub para_id: Option, +} + +#[derive(Debug)] +pub struct RelayChainCli { + /// The actual relay chain cli object. + pub base: polkadot_cli::RunCmd, + + /// Optional chain id that should be passed to the relay chain. + pub chain_id: Option, + + /// The base path that should be used by the relay chain. + pub base_path: PathBuf, +} + +impl RelayChainCli { + /// Parse the relay chain CLI parameters using the para chain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + relay_chain_args: impl Iterator, + ) -> Self { + let extension = crate::chain_spec::Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + let base_path = para_config.base_path.path().join("polkadot"); + Self { + base_path, + chain_id, + base: clap::Parser::parse_from(relay_chain_args), + } + } +} + +/// The `build-spec` command used to build a specification. +#[derive(Debug, Clone, clap::Parser)] +pub struct BuildSpecCmd { + #[clap(flatten)] + pub base: sc_cli::BuildSpecCmd, + + /// Id of the parachain this spec is for. Note that this overrides the `--chain` param. + #[arg(long, conflicts_with = "chain")] + #[arg(long)] + pub parachain_id: Option, + + /// List of bootnodes to add to chain spec + #[arg(long)] + pub add_bootnode: Vec, +} + +impl CliConfiguration for BuildSpecCmd { + fn shared_params(&self) -> &SharedParams { + &self.base.shared_params + } + + fn node_key_params(&self) -> Option<&NodeKeyParams> { + Some(&self.base.node_key_params) + } +} + +pub struct RpcConfig { + pub eth_log_block_cache: usize, + pub eth_statuses_cache: usize, + pub fee_history_limit: u64, + pub max_past_logs: u32, + pub relay_chain_rpc_urls: Vec, +} diff --git a/container-chains/nodes/frontier/src/command.rs b/container-chains/nodes/frontier/src/command.rs new file mode 100644 index 0000000..990d388 --- /dev/null +++ b/container-chains/nodes/frontier/src/command.rs @@ -0,0 +1,523 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::{ + chain_spec, + cli::{Cli, RelayChainCli, Subcommand}, + service::{self, frontier_database_dir, NodeConfig}, + }, + container_chain_template_frontier_runtime::Block, + cumulus_primitives_core::ParaId, + frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}, + log::{info, warn}, + node_common::{command::generate_genesis_block, service::NodeBuilderConfig as _}, + parity_scale_codec::Encode, + polkadot_cli::IdentifyVariant, + sc_cli::{ + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, + NetworkParams, Result, SharedParams, SubstrateCli, + }, + sc_service::{ + config::{BasePath, PrometheusConfig}, + DatabaseSource, + }, + sp_core::hexdisplay::HexDisplay, + sp_runtime::traits::{AccountIdConversion, Block as BlockT}, + std::net::SocketAddr, +}; + +fn load_spec(id: &str, para_id: ParaId) -> std::result::Result, String> { + Ok(match id { + "dev" => Box::new(chain_spec::development_config(para_id, vec![])), + "template-rococo" => Box::new(chain_spec::local_testnet_config(para_id, vec![])), + "" | "local" => Box::new(chain_spec::local_testnet_config(para_id, vec![])), + path => Box::new(chain_spec::ChainSpec::from_json_file( + std::path::PathBuf::from(path), + )?), + }) +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Container Chain Frontier Node".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Container Chain Frontier Node\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/cumulus/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2020 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + load_spec(id, self.para_id.unwrap_or(2000).into()) + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "Container Chain Frontier Node".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Container Chain Frontier Node\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/cumulus/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2020 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) + } +} + +macro_rules! construct_async_run { + (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{ + let runner = $cli.create_runner($cmd)?; + runner.async_run(|mut $config| { + let $components = NodeConfig::new_builder(&mut $config, None)?; + let inner = { $( $code )* }; + + let task_manager = $components.task_manager; + inner.map(|v| (v, task_manager)) + }) + }} +} + +/// Parse command line arguments into service configuration. +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let chain_spec = if let Some(para_id) = cmd.parachain_id { + if cmd.base.shared_params.dev { + Box::new(chain_spec::development_config( + para_id.into(), + cmd.add_bootnode.clone(), + )) + } else { + Box::new(chain_spec::local_testnet_config( + para_id.into(), + cmd.add_bootnode.clone(), + )) + } + } else { + config.chain_spec + }; + cmd.base.run(chain_spec, config.network) + }) + } + Some(Subcommand::CheckBlock(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + let (_, import_queue) = service::import_queue(&config, &components); + Ok(cmd.run(components.client, import_queue)) + }) + } + Some(Subcommand::ExportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, config.database)) + }) + } + Some(Subcommand::ExportState(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, config.chain_spec)) + }) + } + Some(Subcommand::ImportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + let (_, import_queue) = service::import_queue(&config, &components); + Ok(cmd.run(components.client, import_queue)) + }) + } + Some(Subcommand::Revert(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.backend, None)) + }) + } + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| { + // Remove Frontier offchain db + let frontier_database_config = match config.database { + DatabaseSource::RocksDb { .. } => DatabaseSource::RocksDb { + path: frontier_database_dir(&config, "db"), + cache_size: 0, + }, + DatabaseSource::ParityDb { .. } => DatabaseSource::ParityDb { + path: frontier_database_dir(&config, "paritydb"), + }, + _ => { + return Err(format!("Cannot purge `{:?}` database", config.database).into()) + } + }; + + cmd.base.run(frontier_database_config)?; + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()] + .iter() + .chain(cli.relay_chain_args.iter()), + ); + + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + cmd.run(config, polkadot_config) + }) + } + Some(Subcommand::ExportGenesisHead(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + cmd.run(partials.client) + }) + } + Some(Subcommand::ExportGenesisWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + cmd.run(&*spec) + }) + } + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + // Switch on the concrete benchmark sub-command- + match cmd { + BenchmarkCmd::Pallet(cmd) => { + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + Err("Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + .into()) + } + } + BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + cmd.run(partials.client) + }), + #[cfg(not(feature = "runtime-benchmarks"))] + BenchmarkCmd::Storage(_) => Err(sc_cli::Error::Input( + "Compile with --features=runtime-benchmarks \ + to enable storage benchmarks." + .into(), + )), + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + let db = partials.backend.expose_db(); + let storage = partials.backend.expose_storage(); + cmd.run(config, partials.client.clone(), db, storage) + }), + BenchmarkCmd::Machine(cmd) => { + runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())) + } + // NOTE: this allows the Client to leniently implement + // new benchmark commands without requiring a companion MR. + #[allow(unreachable_patterns)] + _ => Err("Benchmarking sub-command unsupported".into()), + } + } + #[cfg(feature = "try-runtime")] + Some(Subcommand::TryRuntime(_)) => { + Err("Substrate's `try-runtime` subcommand has been migrated \ + to a standalone CLI (https://github.com/paritytech/try-runtime-cli)" + .into()) + } + #[cfg(not(feature = "try-runtime"))] + Some(Subcommand::TryRuntime) => { + Err("Substrate's `try-runtime` subcommand has been migrated \ + to a standalone CLI (https://github.com/paritytech/try-runtime-cli)" + .into()) + } + Some(Subcommand::PrecompileWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + Ok(( + cmd.run(partials.backend, config.chain_spec), + partials.task_manager, + )) + }) + } + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + let hwbench = (!cli.no_hardware_benchmarks).then_some( + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench(Some(database_path)) + })).flatten(); + + let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) + .map(|e| e.para_id) + .ok_or("Could not find parachain ID in chain-spec.")?; + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()), + ); + + let rpc_config = crate::cli::RpcConfig { + eth_log_block_cache: cli.run.eth_log_block_cache, + eth_statuses_cache: cli.run.eth_statuses_cache, + fee_history_limit: cli.run.fee_history_limit, + max_past_logs: cli.run.max_past_logs, + relay_chain_rpc_urls: cli.run.base.relay_chain_rpc_urls, + }; + + let extension = chain_spec::Extensions::try_get(&*config.chain_spec); + + let relay_chain_id = extension.map(|e| e.relay_chain.clone()); + + let dev_service = + config.chain_spec.is_dev() || relay_chain_id == Some("dev-service".to_string()); + + let id = ParaId::from(para_id); + + if dev_service { + return crate::service::start_dev_node(config, cli.run.sealing, rpc_config, id, hwbench).await + .map_err(Into::into) + } + + + let parachain_account = + AccountIdConversion::::into_account_truncating(&id); + + // We log both genesis states for reference, as fetching it from runtime would take significant time + let block_state_v0: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V0) + .map_err(|e| format!("{:?}", e))?; + let block_state_v1: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V1) + .map_err(|e| format!("{:?}", e))?; + + let genesis_state_v0 = format!("0x{:?}", HexDisplay::from(&block_state_v0.header().encode())); + let genesis_state_v1 = format!("0x{:?}", HexDisplay::from(&block_state_v1.header().encode())); + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + info!("Parachain id: {:?}", id); + info!("Parachain Account: {}", parachain_account); + info!("Parachain genesis state V0: {}", genesis_state_v0); + info!("Parachain genesis state V1: {}", genesis_state_v1); + + info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + if let cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) = + collator_options.clone().relay_chain_mode { + if !rpc_target_urls.is_empty() && !cli.relay_chain_args.is_empty() { + warn!("Detected relay chain node arguments together with --relay-chain-rpc-url. This command starts a minimal Polkadot node that only uses a network-related subset of all relay chain CLI options."); + } + } + + crate::service::start_parachain_node( + config, + polkadot_config, + collator_options, + id, + rpc_config, + hwbench, + ) + .await + .map(|r| r.0) + .map_err(Into::into) + }) + } + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_listen_port() -> u16 { + 9945 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| Some(self.base_path.clone().into()))) + } + + fn rpc_addr(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_addr(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base + .base + .prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { + self.chain_id.clone().unwrap_or_default() + } else { + chain_id + }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_max_connections(&self) -> Result { + self.base.base.rpc_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } +} diff --git a/container-chains/nodes/frontier/src/main.rs b/container-chains/nodes/frontier/src/main.rs new file mode 100644 index 0000000..984f332 --- /dev/null +++ b/container-chains/nodes/frontier/src/main.rs @@ -0,0 +1,29 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! Substrate Parachain Node Template CLI + +#![warn(missing_docs)] + +mod chain_spec; +mod cli; +mod command; +mod rpc; +mod service; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/container-chains/nodes/frontier/src/rpc/eth.rs b/container-chains/nodes/frontier/src/rpc/eth.rs new file mode 100644 index 0000000..7a9597e --- /dev/null +++ b/container-chains/nodes/frontier/src/rpc/eth.rs @@ -0,0 +1,94 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + sc_network::NetworkService, + sc_network_sync::SyncingService, + sc_transaction_pool::{ChainApi, Pool}, + sp_core::H256, + sp_runtime::traits::Block as BlockT, + std::{collections::BTreeMap, sync::Arc}, +}; +// Frontier +use fc_db::Backend as FrontierBackend; +pub use { + fc_rpc::{EthBlockDataCacheTask, OverrideHandle}, + fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}, + fc_storage::overrides_handle, +}; + +/// Extra dependencies for Ethereum compatibility. +pub struct EthDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Graph pool instance. + pub graph: Arc>, + /// Ethereum transaction converter. + pub converter: Option, + /// The Node authority flag + pub is_authority: bool, + /// Whether to enable dev signer + pub enable_dev_signer: bool, + /// Network service + pub network: Arc>, + /// Chain syncing service + pub sync: Arc>, + /// Frontier Backend. + pub frontier_backend: Arc>, + /// Ethereum data access overrides. + pub overrides: Arc>, + /// Cache for Ethereum block data. + pub block_data_cache: Arc>, + /// EthFilterApi pool. + pub filter_pool: Option, + /// Maximum number of logs in a query. + pub max_past_logs: u32, + /// Fee history cache. + pub fee_history_cache: FeeHistoryCache, + /// Maximum fee history cache size. + pub fee_history_cache_limit: FeeHistoryCacheLimit, + /// Maximum allowed gas limit will be ` block.gas_limit * execute_gas_limit_multiplier` when + /// using eth_call/eth_estimateGas. + pub execute_gas_limit_multiplier: u64, + /// Mandated parent hashes for a given block hash. + pub forced_parent_hashes: Option>, +} + +impl Clone for EthDeps { + fn clone(&self) -> Self { + Self { + client: self.client.clone(), + pool: self.pool.clone(), + graph: self.graph.clone(), + converter: self.converter.clone(), + is_authority: self.is_authority, + enable_dev_signer: self.enable_dev_signer, + network: self.network.clone(), + sync: self.sync.clone(), + frontier_backend: self.frontier_backend.clone(), + overrides: self.overrides.clone(), + block_data_cache: self.block_data_cache.clone(), + filter_pool: self.filter_pool.clone(), + max_past_logs: self.max_past_logs, + fee_history_cache: self.fee_history_cache.clone(), + fee_history_cache_limit: self.fee_history_cache_limit, + execute_gas_limit_multiplier: self.execute_gas_limit_multiplier, + forced_parent_hashes: self.forced_parent_hashes.clone(), + } + } +} diff --git a/container-chains/nodes/frontier/src/rpc/finality.rs b/container-chains/nodes/frontier/src/rpc/finality.rs new file mode 100644 index 0000000..65a2717 --- /dev/null +++ b/container-chains/nodes/frontier/src/rpc/finality.rs @@ -0,0 +1,112 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use fc_rpc::frontier_backend_client::{self, is_canon}; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use sp_blockchain::HeaderBackend; +use sp_core::H256; +use sp_runtime::traits::Block; +use std::{marker::PhantomData, sync::Arc}; + +#[rpc(server)] +#[async_trait::async_trait] +pub trait FrontierFinalityApi { + /// Reports whether a Substrate or Ethereum block is finalized. + /// Returns false if the block is not found. + #[method(name = "frnt_isBlockFinalized")] + async fn is_block_finalized(&self, block_hash: H256) -> RpcResult; + + /// Reports whether an Ethereum transaction is finalized. + /// Returns false if the transaction is not found + #[method(name = "frnt_isTxFinalized")] + async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult; +} + +pub struct FrontierFinality { + pub backend: Arc>, + pub client: Arc, + _phdata: PhantomData, +} + +impl FrontierFinality { + pub fn new(client: Arc, backend: Arc>) -> Self { + Self { + backend, + client, + _phdata: Default::default(), + } + } +} + +#[async_trait::async_trait] +impl FrontierFinalityApiServer for FrontierFinality +where + B: Block, + C: HeaderBackend + Send + Sync + 'static, +{ + async fn is_block_finalized(&self, raw_hash: H256) -> RpcResult { + let client = self.client.clone(); + is_block_finalized_inner::(self.backend.as_ref(), &client, raw_hash).await + } + + async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult { + let client = self.client.clone(); + + if let Some((ethereum_block_hash, _ethereum_index)) = + frontier_backend_client::load_transactions::( + &client, + self.backend.as_ref(), + tx_hash, + true, + ) + .await? + { + is_block_finalized_inner::(self.backend.as_ref(), &client, ethereum_block_hash) + .await + } else { + Ok(false) + } + } +} + +async fn is_block_finalized_inner, C: HeaderBackend + 'static>( + backend: &(dyn fc_api::Backend), + client: &C, + raw_hash: H256, +) -> RpcResult { + let substrate_hash = + match frontier_backend_client::load_hash::(client, backend, raw_hash).await? { + // If we find this hash in the frontier data base, we know it is an eth hash + Some(hash) => hash, + // Otherwise, we assume this is a Substrate hash. + None => raw_hash, + }; + + // First check whether the block is in the best chain + if !is_canon(client, substrate_hash) { + return Ok(false); + } + + // At this point we know the block in question is in the current best chain. + // It's just a question of whether it is in the finalized prefix or not + let query_height = client + .number(substrate_hash) + .expect("No sp_blockchain::Error should be thrown when looking up hash") + .expect("Block is already known to be canon, so it must be in the chain"); + let finalized_height = client.info().finalized_number; + + Ok(query_height <= finalized_height) +} diff --git a/container-chains/nodes/frontier/src/rpc/mod.rs b/container-chains/nodes/frontier/src/rpc/mod.rs new file mode 100644 index 0000000..add2706 --- /dev/null +++ b/container-chains/nodes/frontier/src/rpc/mod.rs @@ -0,0 +1,462 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! A collection of node-specific RPC methods. +//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer +//! used by Substrate nodes. This file extends those RPC definitions with +//! capabilities that are specific to this project's runtime configuration. + +#![warn(missing_docs)] + +pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; + +use { + container_chain_template_frontier_runtime::{opaque::Block, AccountId, Hash, Index}, + cumulus_client_parachain_inherent::ParachainInherentData, + cumulus_primitives_core::{ParaId, PersistedValidationData}, + cumulus_test_relay_sproof_builder::RelayStateSproofBuilder, + fc_rpc::{EthTask, TxPool}, + fc_rpc_core::TxPoolApiServer, + fp_rpc::EthereumRuntimeRPCApi, + futures::StreamExt, + jsonrpsee::RpcModule, + manual_xcm_rpc::{ManualXcm, ManualXcmApiServer}, + sc_client_api::{ + backend::{Backend, StateBackend}, + client::BlockchainEvents, + AuxStore, BlockOf, StorageProvider, + }, + sc_consensus_manual_seal::rpc::{EngineCommand, ManualSeal, ManualSealApiServer}, + sc_network::NetworkService, + sc_network_sync::SyncingService, + sc_service::TaskManager, + sc_transaction_pool::{ChainApi, Pool}, + sc_transaction_pool_api::TransactionPool, + sp_api::{CallApiAt, ProvideRuntimeApi}, + sp_block_builder::BlockBuilder, + sp_blockchain::{ + Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, + }, + sp_consensus_aura::SlotDuration, + sp_core::H256, + sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}, + std::{sync::Arc, time::Duration}, +}; +pub struct DefaultEthConfig(std::marker::PhantomData<(C, BE)>); + +impl fc_rpc::EthConfig for DefaultEthConfig +where + C: StorageProvider + Sync + Send + 'static, + BE: Backend + 'static, +{ + type EstimateGasAdapter = (); + type RuntimeStorageOverride = + fc_rpc::frontier_backend_client::SystemAccountId20StorageOverride; +} + +mod eth; +pub use eth::*; +mod finality; + +/// Full client dependencies. +pub struct FullDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Graph pool instance. + pub graph: Arc>, + /// Whether to deny unsafe calls + pub deny_unsafe: DenyUnsafe, + /// Network service + pub network: Arc>, + /// Chain syncing service + pub sync: Arc>, + /// EthFilterApi pool. + pub filter_pool: Option, + /// Frontier Backend. + // TODO: log indexer? + pub frontier_backend: Arc>, + /// Backend. + pub backend: Arc, + /// Maximum number of logs in a query. + pub max_past_logs: u32, + /// Maximum fee history cache size. + pub fee_history_limit: u64, + /// Fee history cache. + pub fee_history_cache: FeeHistoryCache, + /// Ethereum data access overrides. + pub overrides: Arc>, + /// Cache for Ethereum block data. + pub block_data_cache: Arc>, + /// The Node authority flag + pub is_authority: bool, + /// Manual seal command sink + pub command_sink: Option>>, + /// Channels for manual xcm messages (downward, hrmp) + pub xcm_senders: Option<(flume::Sender>, flume::Sender<(ParaId, Vec)>)>, +} + +/// Instantiate all Full RPC extensions. +pub fn create_full( + deps: FullDeps, + subscription_task_executor: SubscriptionTaskExecutor, + pubsub_notification_sinks: Arc< + fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + >, + >, +) -> Result, Box> +where + BE: Backend + 'static, + BE::State: StateBackend, + BE::Blockchain: BlockchainBackend, + C: ProvideRuntimeApi + StorageProvider + AuxStore, + C: BlockchainEvents, + C: HeaderBackend + HeaderMetadata + 'static, + C: CallApiAt, + C: Send + Sync + 'static, + A: ChainApi + 'static, + C::Api: RuntimeApiCollection, + P: TransactionPool + 'static, +{ + use finality::{FrontierFinality, FrontierFinalityApiServer}; + use { + fc_rpc::{ + Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net, + NetApiServer, Web3, Web3ApiServer, + }, + substrate_frame_rpc_system::{System, SystemApiServer}, + }; + + let mut io = RpcModule::new(()); + let FullDeps { + client, + pool, + graph, + deny_unsafe, + network, + sync, + filter_pool, + frontier_backend, + backend: _, + max_past_logs, + fee_history_limit, + fee_history_cache, + overrides, + block_data_cache, + is_authority, + command_sink, + xcm_senders, + } = deps; + + io.merge(System::new(Arc::clone(&client), Arc::clone(&pool), deny_unsafe).into_rpc())?; + + // TODO: are we supporting signing? + let signers = Vec::new(); + + enum Never {} + impl fp_rpc::ConvertTransaction for Never { + fn convert_transaction(&self, _transaction: pallet_ethereum::Transaction) -> T { + // The Never type is not instantiable, but this method requires the type to be + // instantiated to be called (`&self` parameter), so if the code compiles we have the + // guarantee that this function will never be called. + unreachable!() + } + } + let convert_transaction: Option = None; + let authorities = vec![tc_consensus::get_aura_id_from_seed("alice")]; + let authorities_for_cdp = authorities.clone(); + + let pending_create_inherent_data_providers = move |_, _| { + let authorities_for_cidp = authorities.clone(); + + async move { + let mocked_authorities_noting = + ccp_authorities_noting_inherent::MockAuthoritiesNotingInherentDataProvider { + current_para_block: 1000, + relay_offset: 1000, + relay_blocks_per_para_block: 2, + orchestrator_para_id: 1000u32.into(), + container_para_id: 2000u32.into(), + authorities: authorities_for_cidp, + }; + + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + // Create a dummy parachain inherent data provider which is required to pass + // the checks by the para chain system. We use dummy values because in the 'pending context' + // neither do we have access to the real values nor do we need them. + let (relay_parent_storage_root, relay_chain_state) = RelayStateSproofBuilder { + additional_key_values: mocked_authorities_noting.get_key_values(), + ..Default::default() + } + .into_state_root_and_proof(); + let vfp = PersistedValidationData { + // This is a hack to make `cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases` + // happy. Relay parent number can't be bigger than u32::MAX. + relay_parent_number: u32::MAX, + relay_parent_storage_root, + ..Default::default() + }; + let parachain_inherent_data = ParachainInherentData { + validation_data: vfp, + relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + Ok(( + timestamp, + parachain_inherent_data, + mocked_authorities_noting, + )) + } + }; + + let pending_consensus_data_provider_frontier: Option< + Box<(dyn fc_rpc::pending::ConsensusDataProvider<_>)>, + > = Some(Box::new( + tc_consensus::ContainerManualSealAuraConsensusDataProvider::new( + SlotDuration::from_millis(container_chain_template_frontier_runtime::SLOT_DURATION), + authorities_for_cdp, + ), + )); + + io.merge( + Eth::<_, _, _, _, _, _, _, DefaultEthConfig>::new( + Arc::clone(&client), + Arc::clone(&pool), + Arc::clone(&graph), + convert_transaction, + Arc::clone(&sync), + signers, + Arc::clone(&overrides), + Arc::clone(&frontier_backend), + is_authority, + Arc::clone(&block_data_cache), + fee_history_cache, + fee_history_limit, + 10, + None, + pending_create_inherent_data_providers, + pending_consensus_data_provider_frontier, + ) + .into_rpc(), + )?; + + let tx_pool = TxPool::new(client.clone(), graph.clone()); + if let Some(filter_pool) = filter_pool { + io.merge( + EthFilter::new( + client.clone(), + frontier_backend.clone(), + graph, + filter_pool, + 500_usize, // max stored filters + max_past_logs, + block_data_cache, + ) + .into_rpc(), + )?; + } + + io.merge( + Net::new( + Arc::clone(&client), + network, + // Whether to format the `peer_count` response as Hex (default) or not. + true, + ) + .into_rpc(), + )?; + + if let Some(command_sink) = command_sink { + io.merge( + // We provide the rpc handler with the sending end of the channel to allow the rpc + // send EngineCommands to the background block authorship task. + ManualSeal::new(command_sink).into_rpc(), + )?; + }; + + io.merge(Web3::new(Arc::clone(&client)).into_rpc())?; + io.merge( + EthPubSub::new( + pool, + Arc::clone(&client), + sync, + subscription_task_executor, + overrides, + pubsub_notification_sinks, + ) + .into_rpc(), + )?; + io.merge(tx_pool.into_rpc())?; + + if let Some((downward_message_channel, hrmp_message_channel)) = xcm_senders { + io.merge( + ManualXcm { + downward_message_channel, + hrmp_message_channel, + } + .into_rpc(), + )?; + } + + io.merge(FrontierFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?; + + Ok(io) +} + +pub struct SpawnTasksParams<'a, B: BlockT, C, BE> { + pub task_manager: &'a TaskManager, + pub client: Arc, + pub substrate_backend: Arc, + pub frontier_backend: fc_db::Backend, + pub filter_pool: Option, + pub overrides: Arc>, + pub fee_history_limit: u64, + pub fee_history_cache: FeeHistoryCache, + /// Chain syncing service + pub sync_service: Arc>, + /// Chain syncing service + pub pubsub_notification_sinks: Arc< + fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + >, + >, +} + +use fc_mapping_sync::{kv::MappingSyncWorker, SyncStrategy}; +/// Spawn the tasks that are required to run Moonbeam. +pub fn spawn_essential_tasks(params: SpawnTasksParams) +where + C: ProvideRuntimeApi + BlockOf, + C: HeaderBackend + HeaderMetadata + 'static, + C: BlockchainEvents + StorageProvider, + C: Send + Sync + 'static, + C::Api: EthereumRuntimeRPCApi, + C::Api: BlockBuilder, + B: BlockT + Send + Sync + 'static, + B::Header: HeaderT, + BE: Backend + 'static, + BE::State: StateBackend, +{ + // Frontier offchain DB task. Essential. + // Maps emulated ethereum data to substrate native data. + match params.frontier_backend { + fc_db::Backend::KeyValue(b) => { + params.task_manager.spawn_essential_handle().spawn( + "frontier-mapping-sync-worker", + Some("frontier"), + MappingSyncWorker::new( + params.client.import_notification_stream(), + Duration::new(6, 0), + params.client.clone(), + params.substrate_backend.clone(), + params.overrides.clone(), + Arc::new(b), + 3, + 0, + SyncStrategy::Parachain, + params.sync_service.clone(), + params.pubsub_notification_sinks.clone(), + ) + .for_each(|()| futures::future::ready(())), + ); + } + fc_db::Backend::Sql(b) => { + params.task_manager.spawn_essential_handle().spawn_blocking( + "frontier-mapping-sync-worker", + Some("frontier"), + fc_mapping_sync::sql::SyncWorker::run( + params.client.clone(), + params.substrate_backend.clone(), + Arc::new(b), + params.client.import_notification_stream(), + fc_mapping_sync::sql::SyncWorkerConfig { + read_notification_timeout: Duration::from_secs(10), + check_indexed_blocks_interval: Duration::from_secs(60), + }, + fc_mapping_sync::SyncStrategy::Parachain, + params.sync_service.clone(), + params.pubsub_notification_sinks.clone(), + ), + ); + } + } + + // Frontier `EthFilterApi` maintenance. + // Manages the pool of user-created Filters. + if let Some(filter_pool) = params.filter_pool { + // Each filter is allowed to stay in the pool for 100 blocks. + // TODO: Re-visit this assumption with parathreads, as they + // might have a block every good amount of time, and can be abused + // likely we will need to implement a time-based filter + const FILTER_RETAIN_THRESHOLD: u64 = 100; + params.task_manager.spawn_essential_handle().spawn( + "frontier-filter-pool", + Some("frontier"), + EthTask::filter_pool_task( + Arc::clone(¶ms.client), + filter_pool, + FILTER_RETAIN_THRESHOLD, + ), + ); + } + + // Spawn Frontier FeeHistory cache maintenance task. + params.task_manager.spawn_essential_handle().spawn( + "frontier-fee-history", + Some("frontier"), + EthTask::fee_history_task( + Arc::clone(¶ms.client), + Arc::clone(¶ms.overrides), + params.fee_history_cache, + params.fee_history_limit, + ), + ); +} + +/// A set of APIs that polkadot-like runtimes must implement. +/// +/// This trait has no methods or associated type. It is a concise marker for all the trait bounds +/// that it contains. +pub trait RuntimeApiCollection: + sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::ApiExt + + sp_block_builder::BlockBuilder + + substrate_frame_rpc_system::AccountNonceApi + + sp_api::Metadata + + sp_offchain::OffchainWorkerApi + + sp_session::SessionKeys + + fp_rpc::ConvertTransactionRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi + + cumulus_primitives_core::CollectCollationInfo +{ +} + +impl RuntimeApiCollection for Api where + Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::ApiExt + + sp_block_builder::BlockBuilder + + substrate_frame_rpc_system::AccountNonceApi + + sp_api::Metadata + + sp_offchain::OffchainWorkerApi + + sp_session::SessionKeys + + fp_rpc::ConvertTransactionRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi + + cumulus_primitives_core::CollectCollationInfo +{ +} diff --git a/container-chains/nodes/frontier/src/service.rs b/container-chains/nodes/frontier/src/service.rs new file mode 100644 index 0000000..f28ac64 --- /dev/null +++ b/container-chains/nodes/frontier/src/service.rs @@ -0,0 +1,530 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! Service and ServiceFactory implementation. Specialized wrapper over substrate service. + +#[allow(deprecated)] +use { + container_chain_template_frontier_runtime::{opaque::Block, RuntimeApi}, + cumulus_client_cli::CollatorOptions, + cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport, + cumulus_client_parachain_inherent::{MockValidationDataInherentDataProvider, MockXcmConfig}, + cumulus_client_service::prepare_node_config, + cumulus_primitives_core::{relay_chain::well_known_keys as RelayWellKnownKeys, ParaId}, + fc_consensus::FrontierBlockImport, + fc_db::DatabaseSource, + fc_rpc_core::types::{FeeHistoryCache, FilterPool}, + nimbus_primitives::NimbusId, + node_common::service::{ManualSealConfiguration, NodeBuilder, NodeBuilderConfig, Sealing}, + parity_scale_codec::Encode, + polkadot_parachain_primitives::primitives::HeadData, + sc_consensus::BasicQueue, + sc_executor::WasmExecutor, + sc_service::{Configuration, TFullBackend, TFullClient, TaskManager}, + sp_blockchain::HeaderBackend, + sp_consensus_slots::{Slot, SlotDuration}, + sp_core::{Pair, H256}, + std::{ + collections::BTreeMap, + sync::{Arc, Mutex}, + time::Duration, + }, +}; + +type ParachainExecutor = WasmExecutor; +type ParachainClient = TFullClient; +type ParachainBackend = TFullBackend; +type ParachainBlockImport = TParachainBlockImport< + Block, + FrontierBlockImport, ParachainClient>, + ParachainBackend, +>; + +pub struct NodeConfig; +impl NodeBuilderConfig for NodeConfig { + type Block = Block; + type RuntimeApi = RuntimeApi; + type ParachainExecutor = ParachainExecutor; +} + +pub fn frontier_database_dir(config: &Configuration, path: &str) -> std::path::PathBuf { + let config_dir = config + .base_path + .config_dir(config.chain_spec.id()) + .join("frontier") + .join(path); + + config_dir +} + +// TODO This is copied from frontier. It should be imported instead after +// https://github.com/paritytech/frontier/issues/333 is solved +pub fn open_frontier_backend( + client: Arc, + config: &Configuration, +) -> Result, String> +where + C: sp_blockchain::HeaderBackend, +{ + fc_db::kv::Backend::::new( + client, + &fc_db::kv::DatabaseSettings { + source: match config.database { + DatabaseSource::RocksDb { .. } => DatabaseSource::RocksDb { + path: frontier_database_dir(config, "db"), + cache_size: 0, + }, + DatabaseSource::ParityDb { .. } => DatabaseSource::ParityDb { + path: frontier_database_dir(config, "paritydb"), + }, + DatabaseSource::Auto { .. } => DatabaseSource::Auto { + rocksdb_path: frontier_database_dir(config, "db"), + paritydb_path: frontier_database_dir(config, "paritydb"), + cache_size: 0, + }, + _ => { + return Err("Supported db sources: `rocksdb` | `paritydb` | `auto`".to_string()) + } + }, + }, + ) +} + +thread_local!(static TIMESTAMP: std::cell::RefCell = const { std::cell::RefCell::new(0) }); + +/// Provide a mock duration starting at 0 in millisecond for timestamp inherent. +/// Each call will increment timestamp by slot_duration making Aura think time has passed. +struct MockTimestampInherentDataProvider; +#[async_trait::async_trait] +impl sp_inherents::InherentDataProvider for MockTimestampInherentDataProvider { + async fn provide_inherent_data( + &self, + inherent_data: &mut sp_inherents::InherentData, + ) -> Result<(), sp_inherents::Error> { + TIMESTAMP.with(|x| { + *x.borrow_mut() += container_chain_template_frontier_runtime::SLOT_DURATION; + inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x.borrow()) + }) + } + + async fn try_handle_error( + &self, + _identifier: &sp_inherents::InherentIdentifier, + _error: &[u8], + ) -> Option> { + // The pallet never reports error. + None + } +} + +pub fn import_queue( + parachain_config: &Configuration, + node_builder: &NodeBuilder, +) -> (ParachainBlockImport, BasicQueue) { + let frontier_block_import = + FrontierBlockImport::new(node_builder.client.clone(), node_builder.client.clone()); + + // The parachain block import and import queue + let block_import = cumulus_client_consensus_common::ParachainBlockImport::new( + frontier_block_import, + node_builder.backend.clone(), + ); + let import_queue = nimbus_consensus::import_queue( + node_builder.client.clone(), + block_import.clone(), + move |_, _| async move { + let time = sp_timestamp::InherentDataProvider::from_system_time(); + + Ok((time,)) + }, + &node_builder.task_manager.spawn_essential_handle(), + parachain_config.prometheus_registry(), + false, + ) + .expect("function never fails"); + + (block_import, import_queue) +} + +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with("Parachain")] +async fn start_node_impl( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + rpc_config: crate::cli::RpcConfig, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + let parachain_config = prepare_node_config(parachain_config); + + // Create a `NodeBuilder` which helps setup parachain nodes common systems. + let mut node_builder = NodeConfig::new_builder(¶chain_config, hwbench.clone())?; + + // Frontier specific stuff + let filter_pool: Option = Some(Arc::new(Mutex::new(BTreeMap::new()))); + let fee_history_cache: FeeHistoryCache = Arc::new(Mutex::new(BTreeMap::new())); + let frontier_backend = fc_db::Backend::KeyValue(open_frontier_backend( + node_builder.client.clone(), + ¶chain_config, + )?); + let overrides = crate::rpc::overrides_handle(node_builder.client.clone()); + let fee_history_limit = rpc_config.fee_history_limit; + + let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + > = Default::default(); + let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks); + + let (_, import_queue) = import_queue(¶chain_config, &node_builder); + + // Relay chain interface + let (relay_chain_interface, _collator_key) = node_builder + .build_relay_chain_interface(¶chain_config, polkadot_config, collator_options.clone()) + .await?; + + // Build cumulus network, allowing to access network-related services. + let node_builder = node_builder + .build_cumulus_network( + ¶chain_config, + para_id, + import_queue, + relay_chain_interface.clone(), + ) + .await?; + + crate::rpc::spawn_essential_tasks(crate::rpc::SpawnTasksParams { + task_manager: &node_builder.task_manager, + client: node_builder.client.clone(), + substrate_backend: node_builder.backend.clone(), + frontier_backend: frontier_backend.clone(), + filter_pool: filter_pool.clone(), + overrides: overrides.clone(), + fee_history_limit, + fee_history_cache: fee_history_cache.clone(), + sync_service: node_builder.network.sync_service.clone(), + pubsub_notification_sinks: pubsub_notification_sinks.clone(), + }); + + let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new( + node_builder.task_manager.spawn_handle(), + overrides.clone(), + rpc_config.eth_log_block_cache, + rpc_config.eth_statuses_cache, + node_builder.prometheus_registry.clone(), + )); + + let rpc_builder = { + let client = node_builder.client.clone(); + let pool = node_builder.transaction_pool.clone(); + let pubsub_notification_sinks = pubsub_notification_sinks; + let network = node_builder.network.network.clone(); + let sync = node_builder.network.sync_service.clone(); + let filter_pool = filter_pool.clone(); + let frontier_backend = frontier_backend.clone(); + let backend = node_builder.backend.clone(); + let max_past_logs = rpc_config.max_past_logs; + let overrides = overrides; + let fee_history_cache = fee_history_cache.clone(); + let block_data_cache = block_data_cache; + + Box::new(move |deny_unsafe, subscription_task_executor| { + let deps = crate::rpc::FullDeps { + backend: backend.clone(), + client: client.clone(), + deny_unsafe, + filter_pool: filter_pool.clone(), + frontier_backend: match frontier_backend.clone() { + fc_db::Backend::KeyValue(b) => Arc::new(b), + fc_db::Backend::Sql(b) => Arc::new(b), + }, + graph: pool.pool().clone(), + pool: pool.clone(), + max_past_logs, + fee_history_limit, + fee_history_cache: fee_history_cache.clone(), + network: network.clone(), + sync: sync.clone(), + block_data_cache: block_data_cache.clone(), + overrides: overrides.clone(), + is_authority: false, + command_sink: None, + xcm_senders: None, + }; + crate::rpc::create_full( + deps, + subscription_task_executor, + pubsub_notification_sinks.clone(), + ) + .map_err(Into::into) + }) + }; + + let node_builder = node_builder.spawn_common_tasks(parachain_config, rpc_builder)?; + + let relay_chain_slot_duration = Duration::from_secs(6); + let node_builder = node_builder.start_full_node( + para_id, + relay_chain_interface.clone(), + relay_chain_slot_duration, + )?; + + node_builder.network.start_network.start_network(); + + Ok((node_builder.task_manager, node_builder.client)) +} + +/// Start a parachain node. +pub async fn start_parachain_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + rpc_config: crate::cli::RpcConfig, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + start_node_impl( + parachain_config, + polkadot_config, + collator_options, + para_id, + rpc_config, + hwbench, + ) + .await +} + +/// Helper function to generate a crypto pair from seed +fn get_aura_id_from_seed(seed: &str) -> NimbusId { + sp_core::sr25519::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() + .into() +} + +/// Builds a new development service. This service uses manual seal, and mocks +/// the parachain inherent. +pub async fn start_dev_node( + parachain_config: Configuration, + sealing: Sealing, + rpc_config: crate::cli::RpcConfig, + para_id: ParaId, + hwbench: Option, +) -> Result { + // TODO: Not present before, is this wanted and was forgotten? + // let parachain_config = prepare_node_config(parachain_config); + + // Create a `NodeBuilder` which helps setup parachain nodes common systems. + let node_builder = NodeConfig::new_builder(¶chain_config, hwbench)?; + + // Frontier specific stuff + let filter_pool: Option = Some(Arc::new(Mutex::new(BTreeMap::new()))); + let fee_history_cache: FeeHistoryCache = Arc::new(Mutex::new(BTreeMap::new())); + let frontier_backend = fc_db::Backend::KeyValue(open_frontier_backend( + node_builder.client.clone(), + ¶chain_config, + )?); + let overrides = crate::rpc::overrides_handle(node_builder.client.clone()); + let fee_history_limit = rpc_config.fee_history_limit; + + let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + > = Default::default(); + let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks); + + let (parachain_block_import, import_queue) = import_queue(¶chain_config, &node_builder); + + // Build a Substrate Network. (not cumulus since it is a dev node, it mocks + // the relaychain) + let mut node_builder = node_builder.build_substrate_network(¶chain_config, import_queue)?; + + let mut command_sink = None; + let mut xcm_senders = None; + + if parachain_config.role.is_authority() { + let client = node_builder.client.clone(); + let (downward_xcm_sender, downward_xcm_receiver) = flume::bounded::>(100); + let (hrmp_xcm_sender, hrmp_xcm_receiver) = flume::bounded::<(ParaId, Vec)>(100); + xcm_senders = Some((downward_xcm_sender, hrmp_xcm_sender)); + + let authorities = vec![get_aura_id_from_seed("alice")]; + + command_sink = node_builder.install_manual_seal(ManualSealConfiguration { + block_import: parachain_block_import, + sealing, + soft_deadline: None, + select_chain: sc_consensus::LongestChain::new(node_builder.backend.clone()), + consensus_data_provider: Some(Box::new( + tc_consensus::ContainerManualSealAuraConsensusDataProvider::new( + SlotDuration::from_millis( + container_chain_template_frontier_runtime::SLOT_DURATION, + ), + authorities.clone(), + ), + )), + create_inherent_data_providers: move |block: H256, ()| { + let current_para_block = client + .number(block) + .expect("Header lookup should succeed") + .expect("Header passed in as parent should be present in backend."); + + let hash = client + .hash(current_para_block.saturating_sub(1)) + .expect("Hash of the desired block must be present") + .expect("Hash of the desired block should exist"); + + let para_header = client + .expect_header(hash) + .expect("Expected parachain header should exist") + .encode(); + + let para_head_data: Vec = HeadData(para_header).encode(); + let client_for_xcm = client.clone(); + let authorities_for_cidp = authorities.clone(); + let para_head_key = RelayWellKnownKeys::para_head(para_id); + let relay_slot_key = RelayWellKnownKeys::CURRENT_SLOT.to_vec(); + let slot_duration = container_chain_template_frontier_runtime::SLOT_DURATION; + + let mut timestamp = 0u64; + TIMESTAMP.with(|x| { + timestamp = x.clone().take(); + }); + + timestamp += slot_duration; + + let relay_slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + timestamp.into(), + SlotDuration::from_millis(slot_duration), + ); + let relay_slot = u64::from(*relay_slot); + + let downward_xcm_receiver = downward_xcm_receiver.clone(); + let hrmp_xcm_receiver = hrmp_xcm_receiver.clone(); + + async move { + let mocked_authorities_noting = + ccp_authorities_noting_inherent::MockAuthoritiesNotingInherentDataProvider { + current_para_block, + relay_offset: 1000, + relay_blocks_per_para_block: 2, + orchestrator_para_id: crate::chain_spec::ORCHESTRATOR, + container_para_id: para_id, + authorities: authorities_for_cidp + }; + + let mut additional_keys = mocked_authorities_noting.get_key_values(); + additional_keys.append(&mut vec![(para_head_key, para_head_data), (relay_slot_key, Slot::from(relay_slot).encode())]); + + let time = MockTimestampInherentDataProvider; + let mocked_parachain = MockValidationDataInherentDataProvider { + current_para_block, + relay_offset: 1000, + relay_blocks_per_para_block: 2, + // TODO: Recheck + para_blocks_per_relay_epoch: 10, + relay_randomness_config: (), + xcm_config: MockXcmConfig::new( + &*client_for_xcm, + block, + para_id, + Default::default(), + ), + raw_downward_messages: downward_xcm_receiver.drain().collect(), + raw_horizontal_messages: hrmp_xcm_receiver.drain().collect(), + additional_key_values: Some(additional_keys), + }; + + Ok((time, mocked_parachain, mocked_authorities_noting)) + } + }, + })?; + } + + crate::rpc::spawn_essential_tasks(crate::rpc::SpawnTasksParams { + task_manager: &node_builder.task_manager, + client: node_builder.client.clone(), + substrate_backend: node_builder.backend.clone(), + frontier_backend: frontier_backend.clone(), + filter_pool: filter_pool.clone(), + overrides: overrides.clone(), + fee_history_limit, + fee_history_cache: fee_history_cache.clone(), + sync_service: node_builder.network.sync_service.clone(), + pubsub_notification_sinks: pubsub_notification_sinks.clone(), + }); + + let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new( + node_builder.task_manager.spawn_handle(), + overrides.clone(), + rpc_config.eth_log_block_cache, + rpc_config.eth_statuses_cache, + node_builder.prometheus_registry.clone(), + )); + + let rpc_builder = { + let client = node_builder.client.clone(); + let pool = node_builder.transaction_pool.clone(); + let pubsub_notification_sinks = pubsub_notification_sinks; + let network = node_builder.network.network.clone(); + let sync = node_builder.network.sync_service.clone(); + let filter_pool = filter_pool; + let frontier_backend = frontier_backend; + let backend = node_builder.backend.clone(); + let max_past_logs = rpc_config.max_past_logs; + let overrides = overrides; + let block_data_cache = block_data_cache; + + Box::new(move |deny_unsafe, subscription_task_executor| { + let deps = crate::rpc::FullDeps { + backend: backend.clone(), + client: client.clone(), + deny_unsafe, + filter_pool: filter_pool.clone(), + frontier_backend: match frontier_backend.clone() { + fc_db::Backend::KeyValue(b) => Arc::new(b), + fc_db::Backend::Sql(b) => Arc::new(b), + }, + graph: pool.pool().clone(), + pool: pool.clone(), + max_past_logs, + fee_history_limit, + fee_history_cache: fee_history_cache.clone(), + network: network.clone(), + sync: sync.clone(), + block_data_cache: block_data_cache.clone(), + overrides: overrides.clone(), + is_authority: false, + command_sink: command_sink.clone(), + xcm_senders: xcm_senders.clone(), + }; + crate::rpc::create_full( + deps, + subscription_task_executor, + pubsub_notification_sinks.clone(), + ) + .map_err(Into::into) + }) + }; + + let node_builder = node_builder.spawn_common_tasks(parachain_config, rpc_builder)?; + + log::info!("Development Service Ready"); + + node_builder.network.start_network.start_network(); + Ok(node_builder.task_manager) +} diff --git a/container-chains/nodes/simple/Cargo.toml b/container-chains/nodes/simple/Cargo.toml new file mode 100644 index 0000000..6faf7d6 --- /dev/null +++ b/container-chains/nodes/simple/Cargo.toml @@ -0,0 +1,130 @@ +[package] +name = "container-chain-simple-node" +authors = { workspace = true } +build = "build.rs" +description = "Simple container-chain template node" +edition = "2021" +license = "GPL-3.0-only" +version = "0.7.0" + +[lints] +workspace = true + +[dependencies] +async-io = { workspace = true } +async-trait = { workspace = true } +clap = { workspace = true, features = [ "derive" ] } +flume = { workspace = true } +futures = { workspace = true } +hex-literal = { workspace = true } +jsonrpsee = { workspace = true, features = [ "server" ] } +log = { workspace = true } +parity-scale-codec = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } +serde_json = { workspace = true } +tokio = { workspace = true } +url = { workspace = true } + +# Dancekit +dc-orchestrator-chain-interface = { workspace = true } +dc-orchestrator-chain-rpc-interface = { workspace = true } +dp-core = { workspace = true } + +# Local +ccp-authorities-noting-inherent = { workspace = true } +container-chain-template-simple-runtime = { workspace = true, features = [ "std" ] } +manual-xcm-rpc = { workspace = true } +node-common = { workspace = true } +tc-consensus = { workspace = true } + +# Nimbus +nimbus-consensus = { workspace = true } +nimbus-primitives = { workspace = true, features = [ "std" ] } + +# Substrate +frame-benchmarking = { workspace = true } +frame-benchmarking-cli = { workspace = true } +sc-basic-authorship = { workspace = true } +sc-chain-spec = { workspace = true } +sc-cli = { workspace = true } +sc-client-api = { workspace = true } +sc-consensus = { workspace = true } +sc-consensus-manual-seal = { workspace = true } +sc-executor = { workspace = true } +sc-network = { workspace = true } +sc-network-common = { workspace = true } +sc-network-sync = { workspace = true } +sc-offchain = { workspace = true } +sc-rpc = { workspace = true } +sc-service = { workspace = true } +sc-sysinfo = { workspace = true } +sc-telemetry = { workspace = true } +sc-tracing = { workspace = true } +sc-transaction-pool = { workspace = true } +sc-transaction-pool-api = { workspace = true } +sp-api = { workspace = true, features = [ "std" ] } +sp-block-builder = { workspace = true } +sp-blockchain = { workspace = true } +sp-consensus = { workspace = true } +sp-consensus-aura = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true, features = [ "std" ] } +sp-inherents = { workspace = true, features = [ "std" ] } +sp-io = { workspace = true, features = [ "std" ] } +sp-keystore = { workspace = true, features = [ "std" ] } +sp-offchain = { workspace = true, features = [ "std" ] } +sp-runtime = { workspace = true, features = [ "std" ] } +sp-session = { workspace = true, features = [ "std" ] } +sp-timestamp = { workspace = true, features = [ "std" ] } + +sp-transaction-pool = { workspace = true } +substrate-frame-rpc-system = { workspace = true } +substrate-prometheus-endpoint = { workspace = true } +try-runtime-cli = { workspace = true, optional = true } + +# Polkadot +polkadot-cli = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-primitives = { workspace = true } +polkadot-service = { workspace = true } + +# Cumulus +cumulus-client-cli = { workspace = true } +cumulus-client-consensus-aura = { workspace = true } +cumulus-client-consensus-common = { workspace = true } +cumulus-client-network = { workspace = true } +cumulus-client-parachain-inherent = { workspace = true } +cumulus-client-service = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-parachain-inherent = { workspace = true } +cumulus-relay-chain-interface = { workspace = true } + +pallet-shared-storage = { workspace = true } + + +[build-dependencies] +substrate-build-script-utils = { workspace = true } + +[features] +default = [] +runtime-benchmarks = [ + "container-chain-template-simple-runtime/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking-cli/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "nimbus-primitives/runtime-benchmarks", + "polkadot-cli/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-service/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "container-chain-template-simple-runtime/try-runtime", + "nimbus-primitives/try-runtime", + "polkadot-cli/try-runtime", + "polkadot-service/try-runtime", + "sp-runtime/try-runtime", + "try-runtime-cli/try-runtime", +] diff --git a/container-chains/nodes/simple/build.rs b/container-chains/nodes/simple/build.rs new file mode 100644 index 0000000..cbaa443 --- /dev/null +++ b/container-chains/nodes/simple/build.rs @@ -0,0 +1,23 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/container-chains/nodes/simple/src/chain_spec.rs b/container-chains/nodes/simple/src/chain_spec.rs new file mode 100644 index 0000000..51124b6 --- /dev/null +++ b/container-chains/nodes/simple/src/chain_spec.rs @@ -0,0 +1,212 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + container_chain_template_simple_runtime::{ + AccountId, MaintenanceModeConfig, MigrationsConfig, PolkadotXcmConfig, Signature, + }, + cumulus_primitives_core::ParaId, + sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}, + sc_network::config::MultiaddrWithPeerId, + sc_service::ChainType, + serde::{Deserialize, Serialize}, + sp_core::{sr25519, Pair, Public}, + sp_runtime::traits::{IdentifyAccount, Verify}, +}; + +/// Specialized `ChainSpec` for the normal parachain runtime. +pub type ChainSpec = sc_service::GenericChainSpec< + container_chain_template_simple_runtime::RuntimeGenesisConfig, + Extensions, +>; + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +/// Orcherstrator's parachain id +pub const ORCHESTRATOR: ParaId = ParaId::new(1000); + +/// The extensions for the [`ChainSpec`]. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] +#[serde(deny_unknown_fields)] +pub struct Extensions { + /// The relay chain of the Parachain. + pub relay_chain: String, + /// The id of the Parachain. + pub para_id: u32, +} + +impl Extensions { + /// Try to get the extension from the given `ChainSpec`. + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +pub fn development_config(para_id: ParaId, boot_nodes: Vec) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), false.into()); + + let mut default_funded_accounts = pre_funded_accounts(); + default_funded_accounts.sort(); + default_funded_accounts.dedup(); + let boot_nodes: Vec = boot_nodes + .into_iter() + .map(|x| { + x.parse::() + .unwrap_or_else(|e| panic!("invalid bootnode address format {:?}: {:?}", x, e)) + }) + .collect(); + + ChainSpec::builder( + container_chain_template_simple_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name("Development") + .with_id("dev") + .with_chain_type(ChainType::Development) + .with_genesis_config(testnet_genesis( + default_funded_accounts.clone(), + para_id, + get_account_id_from_seed::("Alice"), + )) + .with_properties(properties) + .with_boot_nodes(boot_nodes) + .build() +} + +pub fn local_testnet_config(para_id: ParaId, boot_nodes: Vec) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), false.into()); + let protocol_id = format!("container-chain-{}", para_id); + + let mut default_funded_accounts = pre_funded_accounts(); + default_funded_accounts.sort(); + default_funded_accounts.dedup(); + let boot_nodes: Vec = boot_nodes + .into_iter() + .map(|x| { + x.parse::() + .unwrap_or_else(|e| panic!("invalid bootnode address format {:?}: {:?}", x, e)) + }) + .collect(); + + ChainSpec::builder( + container_chain_template_simple_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name(&format!("Simple Container {}", para_id)) + .with_id(&format!("simple_container_{}", para_id)) + .with_chain_type(ChainType::Local) + .with_genesis_config(testnet_genesis( + default_funded_accounts.clone(), + para_id, + get_account_id_from_seed::("Alice"), + )) + .with_properties(properties) + .with_protocol_id(&protocol_id) + .with_boot_nodes(boot_nodes) + .build() +} + +fn testnet_genesis( + endowed_accounts: Vec, + id: ParaId, + root_key: AccountId, +) -> serde_json::Value { + let g = container_chain_template_simple_runtime::RuntimeGenesisConfig { + balances: container_chain_template_simple_runtime::BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1 << 60)) + .collect(), + }, + parachain_info: container_chain_template_simple_runtime::ParachainInfoConfig { + parachain_id: id, + ..Default::default() + }, + parachain_system: Default::default(), + sudo: container_chain_template_simple_runtime::SudoConfig { + key: Some(root_key), + }, + authorities_noting: container_chain_template_simple_runtime::AuthoritiesNotingConfig { + orchestrator_para_id: ORCHESTRATOR, + ..Default::default() + }, + migrations: MigrationsConfig::default(), + maintenance_mode: MaintenanceModeConfig { + start_in_maintenance_mode: false, + ..Default::default() + }, + // This should initialize it to whatever we have set in the pallet + polkadot_xcm: PolkadotXcmConfig::default(), + transaction_payment: Default::default(), + tx_pause: Default::default(), + system: Default::default(), + shared_storage: Default::default(), + }; + + serde_json::to_value(g).unwrap() +} + +/// Get pre-funded accounts +pub fn pre_funded_accounts() -> Vec { + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ] +} diff --git a/container-chains/nodes/simple/src/cli.rs b/container-chains/nodes/simple/src/cli.rs new file mode 100644 index 0000000..0472cab --- /dev/null +++ b/container-chains/nodes/simple/src/cli.rs @@ -0,0 +1,201 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + clap::Parser, + node_common::service::Sealing, + sc_cli::{CliConfiguration, NodeKeyParams, SharedParams}, + std::path::PathBuf, + url::Url, +}; + +/// Sub-commands supported by the collator. +#[derive(Debug, clap::Subcommand)] +#[allow(clippy::large_enum_variant)] +pub enum Subcommand { + /// Build a chain specification. + BuildSpec(BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Remove the whole chain. + PurgeChain(cumulus_client_cli::PurgeChainCmd), + + /// Export the genesis state of the parachain. + #[command(alias = "export-genesis-state")] + ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand), + + /// Export the genesis wasm of the parachain. + ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand), + + /// Sub-commands concerned with benchmarking. + /// The pallet benchmarking moved to the `pallet` sub-command. + #[command(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), + + /// Try some testing command against a specified runtime state. + #[cfg(feature = "try-runtime")] + TryRuntime(try_runtime_cli::TryRuntimeCmd), + + /// Errors since the binary was not build with `--features try-runtime`. + #[cfg(not(feature = "try-runtime"))] + TryRuntime, + + /// Precompile the WASM runtime into native code + PrecompileWasm(sc_cli::PrecompileWasmCmd), + + /// Starts in RPC provider mode, watching orchestrator chain for assignements to provide + /// RPC services for container chains. + RpcProvider(RpcProviderSubcommand), +} + +#[derive(Debug, Parser)] +#[group(skip)] +pub struct RunCmd { + #[clap(flatten)] + pub base: cumulus_client_cli::RunCmd, + + /// Id of the parachain this collator collates for. + #[arg(long)] + pub parachain_id: Option, + + /// When blocks should be sealed in the dev service. + /// + /// Options are "instant", "manual", or timer interval in milliseconds + #[arg(long, default_value = "instant")] + pub sealing: Sealing, +} + +impl std::ops::Deref for RunCmd { + type Target = cumulus_client_cli::RunCmd; + + fn deref(&self) -> &Self::Target { + &self.base + } +} + +#[derive(Debug, clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[command(subcommand)] + pub subcommand: Option, + + #[command(flatten)] + pub run: RunCmd, + + /// Disable automatic hardware benchmarks. + /// + /// By default these benchmarks are automatically ran at startup and measure + /// the CPU speed, the memory bandwidth and the disk speed. + /// + /// The results are then printed out in the logs, and also sent as part of + /// telemetry, if telemetry is enabled. + #[arg(long)] + pub no_hardware_benchmarks: bool, + + /// Relay chain arguments + #[arg(raw = true)] + pub relay_chain_args: Vec, + + /// Optional parachain id that should be used to build chain spec. + #[arg(long)] + pub para_id: Option, +} + +#[derive(Debug)] +pub struct RelayChainCli { + /// The actual relay chain cli object. + pub base: polkadot_cli::RunCmd, + + /// Optional chain id that should be passed to the relay chain. + pub chain_id: Option, + + /// The base path that should be used by the relay chain. + pub base_path: PathBuf, +} + +impl RelayChainCli { + /// Parse the relay chain CLI parameters using the para chain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + relay_chain_args: impl Iterator, + ) -> Self { + let extension = crate::chain_spec::Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + let base_path = para_config.base_path.path().join("polkadot"); + Self { + base_path, + chain_id, + base: clap::Parser::parse_from(relay_chain_args), + } + } +} + +/// The `build-spec` command used to build a specification. +#[derive(Debug, Clone, clap::Parser)] +pub struct BuildSpecCmd { + #[clap(flatten)] + pub base: sc_cli::BuildSpecCmd, + + /// Id of the parachain this spec is for. Note that this overrides the `--chain` param. + #[arg(long, conflicts_with = "chain")] + #[arg(long)] + pub parachain_id: Option, + + /// List of bootnodes to add to chain spec + #[arg(long)] + pub add_bootnode: Vec, +} + +impl CliConfiguration for BuildSpecCmd { + fn shared_params(&self) -> &SharedParams { + &self.base.shared_params + } + + fn node_key_params(&self) -> Option<&NodeKeyParams> { + Some(&self.base.node_key_params) + } +} + +#[derive(Debug, clap::Parser)] +#[group(skip)] +pub struct RpcProviderSubcommand { + /// Endpoints to connect to orchestrator nodes, avoiding to start a local orchestrator node. + /// If this list is empty, a local embeded orchestrator node is started. + #[arg(long)] + pub orchestrator_endpoints: Vec, + + /// Account associated with the node, whose assignements will be followed to provide RPC services. + #[arg(long)] + pub assignement_account: dp_core::AccountId, +} diff --git a/container-chains/nodes/simple/src/command.rs b/container-chains/nodes/simple/src/command.rs new file mode 100644 index 0000000..fec9f44 --- /dev/null +++ b/container-chains/nodes/simple/src/command.rs @@ -0,0 +1,531 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::{ + chain_spec, + cli::{Cli, RelayChainCli, Subcommand}, + service::{self, NodeConfig}, + }, + container_chain_template_simple_runtime::Block, + cumulus_primitives_core::ParaId, + dc_orchestrator_chain_interface::OrchestratorChainInterface, + frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}, + futures::stream::StreamExt, + log::{info, warn}, + node_common::{command::generate_genesis_block, service::NodeBuilderConfig as _}, + parity_scale_codec::Encode, + polkadot_service::{IdentifyVariant as _, TaskManager}, + sc_cli::{ + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, + NetworkParams, Result, SharedParams, SubstrateCli, + }, + sc_service::config::{BasePath, PrometheusConfig}, + sp_core::hexdisplay::HexDisplay, + sp_runtime::traits::{AccountIdConversion, Block as BlockT}, + std::net::SocketAddr, +}; + +fn load_spec(id: &str, para_id: ParaId) -> std::result::Result, String> { + Ok(match id { + "dev" => Box::new(chain_spec::development_config(para_id, vec![])), + "template-rococo" => Box::new(chain_spec::local_testnet_config(para_id, vec![])), + "" | "local" => Box::new(chain_spec::local_testnet_config(para_id, vec![])), + path => Box::new(chain_spec::ChainSpec::from_json_file( + std::path::PathBuf::from(path), + )?), + }) +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Container Chain Simple Node".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Container Chain Simple Node\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/cumulus/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2020 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + load_spec(id, self.para_id.unwrap_or(2000).into()) + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "Container Chain Simple Node".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Container Chain Simple Node\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/cumulus/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2020 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) + } +} + +macro_rules! construct_async_run { + (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{ + let runner = $cli.create_runner($cmd)?; + runner.async_run(|$config| { + let $components = NodeConfig::new_builder(&$config, None)?; + let inner = { $( $code )* }; + + let task_manager = $components.task_manager; + inner.map(|v| (v, task_manager)) + }) + }} +} + +/// Parse command line arguments into service configuration. +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let chain_spec = if let Some(para_id) = cmd.parachain_id { + if cmd.base.shared_params.dev { + Box::new(chain_spec::development_config( + para_id.into(), + cmd.add_bootnode.clone(), + )) + } else { + Box::new(chain_spec::local_testnet_config( + para_id.into(), + cmd.add_bootnode.clone(), + )) + } + } else { + config.chain_spec + }; + cmd.base.run(chain_spec, config.network) + }) + } + Some(Subcommand::CheckBlock(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + let (_, import_queue) = service::import_queue(&config, &components); + Ok(cmd.run(components.client, import_queue)) + }) + } + Some(Subcommand::ExportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, config.database)) + }) + } + Some(Subcommand::ExportState(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, config.chain_spec)) + }) + } + Some(Subcommand::ImportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + let (_, import_queue) = service::import_queue(&config, &components); + Ok(cmd.run(components.client, import_queue)) + }) + } + Some(Subcommand::Revert(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.backend, None)) + }) + } + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| { + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()] + .iter() + .chain(cli.relay_chain_args.iter()), + ); + + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + cmd.run(config, polkadot_config) + }) + } + Some(Subcommand::ExportGenesisHead(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + cmd.run(partials.client) + }) + } + Some(Subcommand::ExportGenesisWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + cmd.run(&*spec) + }) + } + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + // Switch on the concrete benchmark sub-command- + match cmd { + BenchmarkCmd::Pallet(cmd) => { + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + Err("Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + .into()) + } + } + BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + cmd.run(partials.client) + }), + #[cfg(not(feature = "runtime-benchmarks"))] + BenchmarkCmd::Storage(_) => Err(sc_cli::Error::Input( + "Compile with --features=runtime-benchmarks \ + to enable storage benchmarks." + .into(), + )), + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + let db = partials.backend.expose_db(); + let storage = partials.backend.expose_storage(); + cmd.run(config, partials.client.clone(), db, storage) + }), + BenchmarkCmd::Machine(cmd) => { + runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())) + } + // NOTE: this allows the Client to leniently implement + // new benchmark commands without requiring a companion MR. + #[allow(unreachable_patterns)] + _ => Err("Benchmarking sub-command unsupported".into()), + } + } + #[cfg(feature = "try-runtime")] + Some(Subcommand::TryRuntime(_)) => { + Err("Substrate's `try-runtime` subcommand has been migrated \ + to a standalone CLI (https://github.com/paritytech/try-runtime-cli)" + .into()) + } + #[cfg(not(feature = "try-runtime"))] + Some(Subcommand::TryRuntime) => { + Err("Substrate's `try-runtime` subcommand has been migrated \ + to a standalone CLI (https://github.com/paritytech/try-runtime-cli)" + .into()) + } + Some(Subcommand::PrecompileWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + Ok(( + cmd.run(partials.backend, config.chain_spec), + partials.task_manager, + )) + }) + } + Some(Subcommand::RpcProvider(cmd)) => { + let runner = cli.create_runner(&cli.run.normalize())?; + + runner.run_node_until_exit(|_config| async move { + let client: Box; + let mut task_manager; + + if cmd.orchestrator_endpoints.is_empty() { + todo!("Start in process node") + } else { + task_manager = TaskManager::new(tokio::runtime::Handle::current(), None) + .map_err(|e| sc_cli::Error::Application(Box::new(e)))?; + + client = dc_orchestrator_chain_rpc_interface::create_client_and_start_worker( + cmd.orchestrator_endpoints.clone(), + &mut task_manager, + None, + ) + .await + .map(Box::new) + .map_err(|e| sc_cli::Error::Application(Box::new(e)))?; + }; + + // POC: Try to fetch some data through the interface. + task_manager + .spawn_handle() + .spawn("rpc_provider_exemple", None, async move { + let mut stream = client.new_best_notification_stream().await.unwrap(); + + while let Some(header) = stream.next().await { + log::info!("New best block: {}", header.hash()); + } + }); + + Ok(task_manager) + }) + } + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + let hwbench = (!cli.no_hardware_benchmarks).then_some( + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench(Some(database_path)) + })).flatten(); + + let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) + .map(|e| e.para_id) + .ok_or("Could not find parachain ID in chain-spec.")?; + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()), + ); + + let extension = chain_spec::Extensions::try_get(&*config.chain_spec); + let relay_chain_id = extension.map(|e| e.relay_chain.clone()); + + let dev_service = + config.chain_spec.is_dev() || relay_chain_id == Some("dev-service".to_string()); + + let id = ParaId::from(para_id); + + if dev_service { + return crate::service::start_dev_node(config, cli.run.sealing, id, hwbench).await + .map_err(Into::into) + } + + + let parachain_account = + AccountIdConversion::::into_account_truncating(&id); + + // We log both genesis states for reference, as fetching it from runtime would take significant time + let block_state_v0: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V0) + .map_err(|e| format!("{:?}", e))?; + let block_state_v1: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V1) + .map_err(|e| format!("{:?}", e))?; + + let genesis_state_v0 = format!("0x{:?}", HexDisplay::from(&block_state_v0.header().encode())); + let genesis_state_v1 = format!("0x{:?}", HexDisplay::from(&block_state_v1.header().encode())); + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + info!("Parachain id: {:?}", id); + info!("Parachain Account: {}", parachain_account); + info!("Parachain genesis state V0: {}", genesis_state_v0); + info!("Parachain genesis state V1: {}", genesis_state_v1); + info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + if let cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) = + collator_options.clone().relay_chain_mode { + if !rpc_target_urls.is_empty() && !cli.relay_chain_args.is_empty() { + warn!("Detected relay chain node arguments together with --relay-chain-rpc-url. This command starts a minimal Polkadot node that only uses a network-related subset of all relay chain CLI options."); + } + } + + crate::service::start_parachain_node( + config, + polkadot_config, + collator_options, + id, + hwbench, + ) + .await + .map(|r| r.0) + .map_err(Into::into) + }) + } + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_listen_port() -> u16 { + 9945 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| Some(self.base_path.clone().into()))) + } + + fn rpc_addr(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_addr(default_listen_port) + } + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base + .base + .prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { + self.chain_id.clone().unwrap_or_default() + } else { + chain_id + }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_max_connections(&self) -> Result { + self.base.base.rpc_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } +} diff --git a/container-chains/nodes/simple/src/main.rs b/container-chains/nodes/simple/src/main.rs new file mode 100644 index 0000000..984f332 --- /dev/null +++ b/container-chains/nodes/simple/src/main.rs @@ -0,0 +1,29 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! Substrate Parachain Node Template CLI + +#![warn(missing_docs)] + +mod chain_spec; +mod cli; +mod command; +mod rpc; +mod service; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/container-chains/nodes/simple/src/rpc.rs b/container-chains/nodes/simple/src/rpc.rs new file mode 100644 index 0000000..a65afce --- /dev/null +++ b/container-chains/nodes/simple/src/rpc.rs @@ -0,0 +1,108 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! A collection of node-specific RPC methods. +//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer +//! used by Substrate nodes. This file extends those RPC definitions with +//! capabilities that are specific to this project's runtime configuration. + +#![warn(missing_docs)] + +pub use sc_rpc::DenyUnsafe; + +use { + container_chain_template_simple_runtime::{opaque::Block, AccountId, Hash, Index as Nonce}, + cumulus_primitives_core::ParaId, + manual_xcm_rpc::{ManualXcm, ManualXcmApiServer as _}, + sc_client_api::AuxStore, + sc_consensus_manual_seal::{ + rpc::{ManualSeal, ManualSealApiServer as _}, + EngineCommand, + }, + sc_transaction_pool_api::TransactionPool, + sp_api::ProvideRuntimeApi, + sp_block_builder::BlockBuilder, + sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}, + std::sync::Arc, +}; + +/// A type representing all RPC extensions. +pub type RpcExtension = jsonrpsee::RpcModule<()>; + +/// Full client dependencies +pub struct FullDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Whether to deny unsafe calls + pub deny_unsafe: DenyUnsafe, + /// Manual seal command sink + pub command_sink: Option>>, + /// Channels for manual xcm messages (downward, hrmp) + pub xcm_senders: Option<(flume::Sender>, flume::Sender<(ParaId, Vec)>)>, +} + +/// Instantiate all RPC extensions. +pub fn create_full( + deps: FullDeps, +) -> Result> +where + C: ProvideRuntimeApi + + HeaderBackend + + AuxStore + + HeaderMetadata + + Send + + Sync + + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi, + C::Api: BlockBuilder, + P: TransactionPool + Sync + Send + 'static, +{ + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let mut module = RpcExtension::new(()); + let FullDeps { + client, + pool, + deny_unsafe, + command_sink, + xcm_senders, + } = deps; + + module.merge(System::new(client, pool, deny_unsafe).into_rpc())?; + + // Manual seal + if let Some(command_sink) = command_sink { + module.merge( + // We provide the rpc handler with the sending end of the channel to allow the rpc + // send EngineCommands to the background block authorship task. + ManualSeal::new(command_sink).into_rpc(), + )?; + }; + + if let Some((downward_message_channel, hrmp_message_channel)) = xcm_senders { + module.merge( + ManualXcm { + downward_message_channel, + hrmp_message_channel, + } + .into_rpc(), + )?; + } + + Ok(module) +} diff --git a/container-chains/nodes/simple/src/service.rs b/container-chains/nodes/simple/src/service.rs new file mode 100644 index 0000000..c6ba66e --- /dev/null +++ b/container-chains/nodes/simple/src/service.rs @@ -0,0 +1,328 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! Service and ServiceFactory implementation. Specialized wrapper over substrate service. + +#[allow(deprecated)] +use { + container_chain_template_simple_runtime::{opaque::Block, RuntimeApi}, + cumulus_client_cli::CollatorOptions, + cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport, + cumulus_client_parachain_inherent::{MockValidationDataInherentDataProvider, MockXcmConfig}, + cumulus_client_service::prepare_node_config, + cumulus_primitives_core::{relay_chain::well_known_keys as RelayWellKnownKeys, ParaId}, + nimbus_primitives::NimbusId, + node_common::service::ManualSealConfiguration, + node_common::service::Sealing, + node_common::service::{NodeBuilder, NodeBuilderConfig}, + parity_scale_codec::Encode, + polkadot_parachain_primitives::primitives::HeadData, + sc_consensus::BasicQueue, + sc_executor::WasmExecutor, + sc_service::{Configuration, TFullBackend, TFullClient, TaskManager}, + sp_blockchain::HeaderBackend, + sp_consensus_slots::{Slot, SlotDuration}, + sp_core::Pair, + sp_core::H256, + std::{sync::Arc, time::Duration}, +}; + +type ParachainExecutor = WasmExecutor; +type ParachainClient = TFullClient; +type ParachainBackend = TFullBackend; +type ParachainBlockImport = TParachainBlockImport, ParachainBackend>; + +pub struct NodeConfig; +impl NodeBuilderConfig for NodeConfig { + type Block = Block; + type RuntimeApi = RuntimeApi; + type ParachainExecutor = ParachainExecutor; +} + +thread_local!(static TIMESTAMP: std::cell::RefCell = const { std::cell::RefCell::new(0) }); + +/// Provide a mock duration starting at 0 in millisecond for timestamp inherent. +/// Each call will increment timestamp by slot_duration making Aura think time has passed. +struct MockTimestampInherentDataProvider; +#[async_trait::async_trait] +impl sp_inherents::InherentDataProvider for MockTimestampInherentDataProvider { + async fn provide_inherent_data( + &self, + inherent_data: &mut sp_inherents::InherentData, + ) -> Result<(), sp_inherents::Error> { + TIMESTAMP.with(|x| { + *x.borrow_mut() += container_chain_template_simple_runtime::SLOT_DURATION; + inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x.borrow()) + }) + } + + async fn try_handle_error( + &self, + _identifier: &sp_inherents::InherentIdentifier, + _error: &[u8], + ) -> Option> { + // The pallet never reports error. + None + } +} + +pub fn import_queue( + parachain_config: &Configuration, + node_builder: &NodeBuilder, +) -> (ParachainBlockImport, BasicQueue) { + // The nimbus import queue ONLY checks the signature correctness + // Any other checks corresponding to the author-correctness should be done + // in the runtime + let block_import = + ParachainBlockImport::new(node_builder.client.clone(), node_builder.backend.clone()); + + let import_queue = nimbus_consensus::import_queue( + node_builder.client.clone(), + block_import.clone(), + move |_, _| async move { + let time = sp_timestamp::InherentDataProvider::from_system_time(); + + Ok((time,)) + }, + &node_builder.task_manager.spawn_essential_handle(), + parachain_config.prometheus_registry(), + false, + ) + .expect("function never fails"); + + (block_import, import_queue) +} + +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with("Parachain")] +pub async fn start_parachain_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + let parachain_config = prepare_node_config(parachain_config); + + // Create a `NodeBuilder` which helps setup parachain nodes common systems. + let mut node_builder = NodeConfig::new_builder(¶chain_config, hwbench.clone())?; + + let (_, import_queue) = import_queue(¶chain_config, &node_builder); + + // Relay chain interface + let (relay_chain_interface, _collator_key) = node_builder + .build_relay_chain_interface(¶chain_config, polkadot_config, collator_options.clone()) + .await?; + + // Build cumulus network, allowing to access network-related services. + let node_builder = node_builder + .build_cumulus_network( + ¶chain_config, + para_id, + import_queue, + relay_chain_interface.clone(), + ) + .await?; + + let rpc_builder = { + let client = node_builder.client.clone(); + let transaction_pool = node_builder.transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = crate::rpc::FullDeps { + client: client.clone(), + pool: transaction_pool.clone(), + deny_unsafe, + command_sink: None, + xcm_senders: None, + }; + + crate::rpc::create_full(deps).map_err(Into::into) + }) + }; + + let node_builder = node_builder.spawn_common_tasks(parachain_config, rpc_builder)?; + + let relay_chain_slot_duration = Duration::from_secs(6); + let node_builder = node_builder.start_full_node( + para_id, + relay_chain_interface.clone(), + relay_chain_slot_duration, + )?; + + node_builder.network.start_network.start_network(); + + Ok((node_builder.task_manager, node_builder.client)) +} + +/// Helper function to generate a crypto pair from seed +fn get_aura_id_from_seed(seed: &str) -> NimbusId { + sp_core::sr25519::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() + .into() +} + +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with("Parachain Dev Node")] +pub async fn start_dev_node( + parachain_config: Configuration, + sealing: Sealing, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result { + let parachain_config = prepare_node_config(parachain_config); + + // Create a `NodeBuilder` which helps setup parachain nodes common systems. + let node_builder = NodeConfig::new_builder(¶chain_config, hwbench.clone())?; + + let (parachain_block_import, import_queue) = import_queue(¶chain_config, &node_builder); + + // Build a Substrate Network. (not cumulus since it is a dev node, it mocks + // the relaychain) + let mut node_builder = node_builder.build_substrate_network(¶chain_config, import_queue)?; + + let mut command_sink = None; + let mut xcm_senders = None; + + if parachain_config.role.is_authority() { + let client = node_builder.client.clone(); + let (downward_xcm_sender, downward_xcm_receiver) = flume::bounded::>(100); + let (hrmp_xcm_sender, hrmp_xcm_receiver) = flume::bounded::<(ParaId, Vec)>(100); + xcm_senders = Some((downward_xcm_sender, hrmp_xcm_sender)); + + let authorities = vec![get_aura_id_from_seed("alice")]; + + command_sink = node_builder.install_manual_seal(ManualSealConfiguration { + block_import: parachain_block_import, + sealing, + soft_deadline: None, + select_chain: sc_consensus::LongestChain::new(node_builder.backend.clone()), + consensus_data_provider: Some(Box::new( + tc_consensus::ContainerManualSealAuraConsensusDataProvider::new( + SlotDuration::from_millis( + container_chain_template_simple_runtime::SLOT_DURATION, + ), + authorities.clone(), + ), + )), + create_inherent_data_providers: move |block: H256, ()| { + let current_para_block = client + .number(block) + .expect("Header lookup should succeed") + .expect("Header passed in as parent should be present in backend."); + + let hash = client + .hash(current_para_block.saturating_sub(1)) + .expect("Hash of the desired block must be present") + .expect("Hash of the desired block should exist"); + + let para_header = client + .expect_header(hash) + .expect("Expected parachain header should exist") + .encode(); + + let para_head_data: Vec = HeadData(para_header).encode(); + let client_for_xcm = client.clone(); + let authorities_for_cidp = authorities.clone(); + let para_head_key = RelayWellKnownKeys::para_head(para_id); + let relay_slot_key = RelayWellKnownKeys::CURRENT_SLOT.to_vec(); + let slot_duration = container_chain_template_simple_runtime::SLOT_DURATION; + + let mut timestamp = 0u64; + TIMESTAMP.with(|x| { + timestamp = x.clone().take(); + }); + + timestamp += slot_duration; + + let relay_slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + timestamp.into(), + SlotDuration::from_millis(slot_duration), + ); + let relay_slot = u64::from(*relay_slot); + + let downward_xcm_receiver = downward_xcm_receiver.clone(); + let hrmp_xcm_receiver = hrmp_xcm_receiver.clone(); + + async move { + let mocked_authorities_noting = + ccp_authorities_noting_inherent::MockAuthoritiesNotingInherentDataProvider { + current_para_block, + relay_offset: 1000, + relay_blocks_per_para_block: 2, + orchestrator_para_id: crate::chain_spec::ORCHESTRATOR, + container_para_id: para_id, + authorities: authorities_for_cidp + }; + + let mut additional_keys = mocked_authorities_noting.get_key_values(); + additional_keys.append(&mut vec![(para_head_key, para_head_data), (relay_slot_key, Slot::from(relay_slot).encode())]); + + let time = MockTimestampInherentDataProvider; + let mocked_parachain = MockValidationDataInherentDataProvider { + current_para_block, + relay_offset: 1000, + relay_blocks_per_para_block: 2, + // TODO: Recheck + para_blocks_per_relay_epoch: 10, + relay_randomness_config: (), + xcm_config: MockXcmConfig::new( + &*client_for_xcm, + block, + para_id, + Default::default(), + ), + raw_downward_messages: downward_xcm_receiver.drain().collect(), + raw_horizontal_messages: hrmp_xcm_receiver.drain().collect(), + additional_key_values: Some(additional_keys), + }; + + Ok((time, mocked_parachain, mocked_authorities_noting)) + } + }, + })?; + } + + let rpc_builder = { + let client = node_builder.client.clone(); + let transaction_pool = node_builder.transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = crate::rpc::FullDeps { + client: client.clone(), + pool: transaction_pool.clone(), + deny_unsafe, + command_sink: command_sink.clone(), + xcm_senders: xcm_senders.clone(), + }; + + crate::rpc::create_full(deps).map_err(Into::into) + }) + }; + + let node_builder = node_builder.spawn_common_tasks(parachain_config, rpc_builder)?; + + log::info!("Development Service Ready"); + + node_builder.network.start_network.start_network(); + + Ok(node_builder.task_manager) +} diff --git a/container-chains/runtime-templates/frontier/Cargo.toml b/container-chains/runtime-templates/frontier/Cargo.toml new file mode 100644 index 0000000..79d0fb7 --- /dev/null +++ b/container-chains/runtime-templates/frontier/Cargo.toml @@ -0,0 +1,310 @@ +[package] +name = "container-chain-template-frontier-runtime" +authors = { workspace = true } +description = "Frontier container chain template runtime" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +hex-literal = { workspace = true, optional = true } +log = { workspace = true } +num_enum = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive" ] } +scale-info = { workspace = true, features = [ "derive" ] } +serde = { workspace = true, optional = true, features = [ "derive" ] } +smallvec = { workspace = true } + +# Local +ccp-xcm = { workspace = true } +dp-consensus = { workspace = true } +dp-impl-tanssi-pallets-config = { workspace = true } +dp-slot-duration-runtime-api = { workspace = true } +pallet-cc-authorities-noting = { workspace = true } +runtime-common = { workspace = true } + +# Moonkit +async-backing-primitives = { workspace = true } +nimbus-primitives = { workspace = true } +pallet-async-backing = { workspace = true } +pallet-author-inherent = { workspace = true } +pallet-evm-precompile-balances-erc20 = { workspace = true } +pallet-evm-precompile-batch = { workspace = true } +pallet-evm-precompile-call-permit = { workspace = true } +pallet-evm-precompile-xcm-utils = { workspace = true } +pallet-evm-precompileset-assets-erc20 = { workspace = true } +pallet-foreign-asset-creator = { workspace = true } +pallet-maintenance-mode = { workspace = true, features = [ "xcm-support" ] } +pallet-migrations = { workspace = true } +xcm-primitives = { workspace = true } + +# Dancekit +pallet-xcm-executor-utils = { workspace = true } + +# Substrate +frame-executive = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-system-rpc-runtime-api = { workspace = true } +frame-try-runtime = { workspace = true, optional = true } +pallet-asset-rate = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true, features = [ "insecure_zero_ed" ] } +pallet-message-queue = { workspace = true } +pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } +pallet-root-testing = { workspace = true } +pallet-sudo = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-transaction-payment = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } +pallet-tx-pause = { workspace = true } +pallet-utility = { workspace = true } +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-consensus-aura = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true } +sp-debug-derive = { workspace = true } +sp-genesis-builder = { workspace = true } +sp-inherents = { workspace = true } +sp-offchain = { workspace = true } +sp-runtime = { workspace = true } +sp-session = { workspace = true } +sp-std = { workspace = true } +sp-transaction-pool = { workspace = true } +sp-trie = { workspace = true } + +sp-version = { workspace = true } + +# Polkadot +pallet-xcm = { workspace = true } +pallet-xcm-benchmarks = { workspace = true, optional = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-runtime-common = { workspace = true } +staging-xcm = { workspace = true } +staging-xcm-builder = { workspace = true } +staging-xcm-executor = { workspace = true } + +# Cumulus +cumulus-pallet-dmp-queue = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true } +cumulus-pallet-session-benchmarking = { workspace = true } +cumulus-pallet-xcm = { workspace = true } +cumulus-pallet-xcmp-queue = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-timestamp = { workspace = true } +cumulus-primitives-utility = { workspace = true } +parachain-info = { workspace = true } +parachains-common = { workspace = true } + +# Frontier +fp-account = { workspace = true, features = [ "serde" ] } +fp-evm = { workspace = true, features = [ "serde" ] } +fp-rpc = { workspace = true } +fp-self-contained = { workspace = true, features = [ "serde" ] } +pallet-base-fee = { workspace = true } +pallet-ethereum = { workspace = true } +pallet-evm = { workspace = true } +pallet-evm-chain-id = { workspace = true } +pallet-evm-precompile-modexp = { workspace = true } +pallet-evm-precompile-sha3fips = { workspace = true } +pallet-evm-precompile-simple = { workspace = true } +precompile-utils = { workspace = true } + +# Benchmarking +frame-benchmarking = { workspace = true, optional = true } +frame-system-benchmarking = { workspace = true, optional = true } + +[build-dependencies] +substrate-wasm-builder = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "async-backing-primitives/std", + "ccp-xcm/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-session-benchmarking/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "dp-consensus/std", + "dp-impl-tanssi-pallets-config/std", + "dp-slot-duration-runtime-api/std", + "fp-account/std", + "fp-evm/std", + "fp-rpc/std", + "fp-self-contained/std", + "frame-benchmarking?/std", + "frame-executive/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "frame-try-runtime/std", + "log/std", + "nimbus-primitives/std", + "num_enum/std", + "pallet-asset-rate/std", + "pallet-assets/std", + "pallet-async-backing/std", + "pallet-author-inherent/std", + "pallet-balances/std", + "pallet-base-fee/std", + "pallet-cc-authorities-noting/std", + "pallet-ethereum/std", + "pallet-ethereum/std", + "pallet-evm-chain-id/std", + "pallet-evm-precompile-balances-erc20/std", + "pallet-evm-precompile-batch/std", + "pallet-evm-precompile-call-permit/std", + "pallet-evm-precompile-modexp/std", + "pallet-evm-precompile-sha3fips/std", + "pallet-evm-precompile-simple/std", + "pallet-evm-precompile-xcm-utils/std", + "pallet-evm-precompileset-assets-erc20/std", + "pallet-evm/std", + "pallet-foreign-asset-creator/std", + "pallet-maintenance-mode/std", + "pallet-message-queue/std", + "pallet-migrations/std", + "pallet-multisig/std", + "pallet-proxy/std", + "pallet-root-testing/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-tx-pause/std", + "pallet-utility/std", + "pallet-xcm-benchmarks?/std", + "pallet-xcm-executor-utils/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "parity-scale-codec/std", + "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", + "precompile-utils/std", + "runtime-common/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-consensus-slots/std", + "sp-core/std", + "sp-debug-derive/std", + "sp-genesis-builder/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-trie/std", + "sp-version/std", + "staging-xcm-builder/std", + "staging-xcm-executor/std", + "staging-xcm/std", + "xcm-primitives/std", +] + +# Allow to print logs details (no wasm:stripped) +force-debug = [ "sp-debug-derive/force-debug" ] + +runtime-benchmarks = [ + "cumulus-pallet-dmp-queue/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-utility/runtime-benchmarks", + "dp-consensus/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "hex-literal", + "nimbus-primitives/runtime-benchmarks", + "pallet-asset-rate/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-author-inherent/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-cc-authorities-noting/runtime-benchmarks", + "pallet-ethereum/runtime-benchmarks", + "pallet-evm-precompile-xcm-utils/runtime-benchmarks", + "pallet-evm/runtime-benchmarks", + "pallet-foreign-asset-creator/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-tx-pause/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-executor-utils/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "runtime-common/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "staging-xcm-builder/runtime-benchmarks", + "staging-xcm-executor/runtime-benchmarks", + "xcm-primitives/runtime-benchmarks", +] + +try-runtime = [ + "cumulus-pallet-dmp-queue/try-runtime", + "cumulus-pallet-parachain-system/try-runtime", + "cumulus-pallet-xcm/try-runtime", + "cumulus-pallet-xcmp-queue/try-runtime", + "fp-self-contained/try-runtime", + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "nimbus-primitives/try-runtime", + "pallet-asset-rate/try-runtime", + "pallet-assets/try-runtime", + "pallet-async-backing/try-runtime", + "pallet-author-inherent/try-runtime", + "pallet-balances/try-runtime", + "pallet-base-fee/try-runtime", + "pallet-cc-authorities-noting/try-runtime", + "pallet-ethereum/try-runtime", + "pallet-evm-chain-id/try-runtime", + "pallet-evm/try-runtime", + "pallet-foreign-asset-creator/try-runtime", + "pallet-maintenance-mode/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", + "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", + "pallet-root-testing/try-runtime", + "pallet-sudo/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "pallet-tx-pause/try-runtime", + "pallet-utility/try-runtime", + "pallet-xcm-executor-utils/try-runtime", + "pallet-xcm/try-runtime", + "parachain-info/try-runtime", + "polkadot-runtime-common/try-runtime", + "runtime-common/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/container-chains/runtime-templates/frontier/build.rs b/container-chains/runtime-templates/frontier/build.rs new file mode 100644 index 0000000..9e48e37 --- /dev/null +++ b/container-chains/runtime-templates/frontier/build.rs @@ -0,0 +1,25 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/container-chains/runtime-templates/frontier/src/impl_on_charge_evm_transaction.rs b/container-chains/runtime-templates/frontier/src/impl_on_charge_evm_transaction.rs new file mode 100644 index 0000000..8c88d72 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/impl_on_charge_evm_transaction.rs @@ -0,0 +1,64 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +#[macro_export] +macro_rules! impl_on_charge_evm_transaction { + {} => { + type CurrencyAccountId = ::AccountId; + + type BalanceFor = + <::Currency as CurrencyT>>::Balance; + + type PositiveImbalanceFor = + <::Currency as CurrencyT>>::PositiveImbalance; + + type NegativeImbalanceFor = + <::Currency as CurrencyT>>::NegativeImbalance; + + pub struct OnChargeEVMTransaction(sp_std::marker::PhantomData); + impl OnChargeEVMTransactionT for OnChargeEVMTransaction + where + T: pallet_evm::Config, + PositiveImbalanceFor: Imbalance, Opposite = NegativeImbalanceFor>, + NegativeImbalanceFor: Imbalance, Opposite = PositiveImbalanceFor>, + OU: OnUnbalanced>, + U256: UniqueSaturatedInto> + { + type LiquidityInfo = Option>; + + fn withdraw_fee(who: &H160, fee: U256) -> Result> { + EVMCurrencyAdapter::<::Currency, ()>::withdraw_fee(who, fee) + } + + fn correct_and_deposit_fee( + who: &H160, + corrected_fee: U256, + base_fee: U256, + already_withdrawn: Self::LiquidityInfo, + ) -> Self::LiquidityInfo { + ::Currency, OU> as OnChargeEVMTransactionT< + T, + >>::correct_and_deposit_fee(who, corrected_fee, base_fee, already_withdrawn) + } + + fn pay_priority_fee(tip: Self::LiquidityInfo) { + if let Some(tip) = tip { + OU::on_unbalanced(tip); + } + } + } + } +} diff --git a/container-chains/runtime-templates/frontier/src/lib.rs b/container-chains/runtime-templates/frontier/src/lib.rs new file mode 100644 index 0000000..b369b3a --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/lib.rs @@ -0,0 +1,1558 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; +#[cfg(feature = "std")] +use sp_version::NativeVersion; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +pub mod migrations; +mod precompiles; +pub mod weights; +pub mod xcm_config; + +use { + crate::precompiles::TemplatePrecompiles, + cumulus_primitives_core::AggregateMessageOrigin, + dp_impl_tanssi_pallets_config::impl_tanssi_pallets_config, + fp_account::EthereumSignature, + fp_evm::weight_per_gas, + fp_rpc::TransactionStatus, + frame_support::{ + construct_runtime, + dispatch::{DispatchClass, GetDispatchInfo}, + genesis_builder_helper::{build_config, create_default_config}, + pallet_prelude::DispatchResult, + parameter_types, + traits::{ + ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Contains, Currency as CurrencyT, + FindAuthor, Imbalance, InsideBoth, InstanceFilter, OnFinalize, OnUnbalanced, + }, + weights::{ + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, + WEIGHT_REF_TIME_PER_SECOND, + }, + ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, + }, + }, + frame_system::{ + limits::{BlockLength, BlockWeights}, + EnsureRoot, + }, + nimbus_primitives::{NimbusId, SlotBeacon}, + pallet_ethereum::{Call::transact, PostLogContent, Transaction as EthereumTransaction}, + pallet_evm::{ + Account as EVMAccount, EVMCurrencyAdapter, EnsureAddressNever, EnsureAddressRoot, + FeeCalculator, GasWeightMapping, IdentityAddressMapping, + OnChargeEVMTransaction as OnChargeEVMTransactionT, Runner, + }, + pallet_transaction_payment::CurrencyAdapter, + parity_scale_codec::{Decode, Encode}, + polkadot_runtime_common::SlowAdjustingFeeUpdate, + scale_info::TypeInfo, + smallvec::smallvec, + sp_api::impl_runtime_apis, + sp_consensus_slots::{Slot, SlotDuration}, + sp_core::{Get, MaxEncodedLen, OpaqueMetadata, H160, H256, U256}, + sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{ + BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, IdentifyAccount, + IdentityLookup, PostDispatchInfoOf, UniqueSaturatedInto, Verify, + }, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, + }, + ApplyExtrinsicResult, + }, + sp_std::prelude::*, + sp_version::RuntimeVersion, +}; +pub use { + sp_consensus_aura::sr25519::AuthorityId as AuraId, + sp_runtime::{MultiAddress, Perbill, Permill}, +}; + +// Polkadot imports +use polkadot_runtime_common::BlockHashCount; + +pub type Precompiles = TemplatePrecompiles; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = EthereumSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Balance of an account. +pub type Balance = u128; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// An index to a block. +pub type BlockNumber = u32; + +/// The address format for describing accounts. +pub type Address = AccountId; + +/// Block header type as expected by this runtime. +pub type Header = generic::Header; + +/// Block type as expected by this runtime. +pub type Block = generic::Block; + +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; + +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); + +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + fp_self_contained::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = + fp_self_contained::CheckedExtrinsic; +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +pub mod currency { + use super::Balance; + + pub const MICROUNIT: Balance = 1_000_000_000_000; + pub const MILLIUNIT: Balance = 1_000_000_000_000_000; + pub const UNIT: Balance = 1_000_000_000_000_000_000; + pub const KILOUNIT: Balance = 1_000_000_000_000_000_000_000; + + pub const STORAGE_BYTE_FEE: Balance = 100 * MICROUNIT; + + pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 100 * MILLIUNIT + (bytes as Balance) * STORAGE_BYTE_FEE + } +} + +impl fp_self_contained::SelfContainedCall for RuntimeCall { + type SignedInfo = H160; + + fn is_self_contained(&self) -> bool { + match self { + RuntimeCall::Ethereum(call) => call.is_self_contained(), + _ => false, + } + } + + fn check_self_contained(&self) -> Option> { + match self { + RuntimeCall::Ethereum(call) => call.check_self_contained(), + _ => None, + } + } + + fn validate_self_contained( + &self, + info: &Self::SignedInfo, + dispatch_info: &DispatchInfoOf, + len: usize, + ) -> Option { + match self { + RuntimeCall::Ethereum(call) => call.validate_self_contained(info, dispatch_info, len), + _ => None, + } + } + + fn pre_dispatch_self_contained( + &self, + info: &Self::SignedInfo, + dispatch_info: &DispatchInfoOf, + len: usize, + ) -> Option> { + match self { + RuntimeCall::Ethereum(call) => { + call.pre_dispatch_self_contained(info, dispatch_info, len) + } + _ => None, + } + } + + fn apply_self_contained( + self, + info: Self::SignedInfo, + ) -> Option>> { + match self { + call @ RuntimeCall::Ethereum(pallet_ethereum::Call::transact { .. }) => { + Some(call.dispatch(RuntimeOrigin::from( + pallet_ethereum::RawOrigin::EthereumTransaction(info), + ))) + } + _ => None, + } + } +} + +#[derive(Clone)] +pub struct TransactionConverter; + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } +} + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction( + &self, + transaction: pallet_ethereum::Transaction, + ) -> opaque::UncheckedExtrinsic { + let extrinsic = UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ); + let encoded = extrinsic.encode(); + opaque::UncheckedExtrinsic::decode(&mut &encoded[..]) + .expect("Encoded extrinsic is always valid") + } +} + +/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the +/// node's balance type. +/// +/// This should typically create a mapping between the following ranges: +/// - `[0, MAXIMUM_BLOCK_WEIGHT]` +/// - `[Balance::min, Balance::max]` +/// +/// Yet, it can be used for any other sort of change to weight-fee. Some examples being: +/// - Setting it to `0` will essentially disable the weight fee. +/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients { + // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT: + // in our template, we map to 1/10 of that, or 1/10 MILLIUNIT + let p = currency::MILLIUNIT / 10; + let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); + smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } +} + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use { + super::*, + sp_runtime::{generic, traits::BlakeTwo256}, + }; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +mod impl_on_charge_evm_transaction; + +impl_opaque_keys! { + pub struct SessionKeys { } +} + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("frontier-template"), + impl_name: create_runtime_str!("frontier-template"), + authoring_version: 1, + spec_version: 700, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// This determines the average expected block time that we are targeting. +/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. +/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked +/// up by `pallet_aura` to implement `fn slot_duration()`. +/// +/// Change this to adjust the block time. +pub const MILLISECS_PER_BLOCK: u64 = 6000; + +// NOTE: Currently it is not possible to change the slot duration after the chain has started. +// Attempting to do so will brick block production. +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +// Time is measured by number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; + +/// The existential deposit. Set to 0 because this is an ethereum-like chain +/// We set this to one for runtime-benchmarks because plenty of the benches we +/// incorporate from parity assume ED != 0 +#[cfg(feature = "runtime-benchmarks")] +pub const EXISTENTIAL_DEPOSIT: Balance = 1 * currency::MILLIUNIT; +#[cfg(not(feature = "runtime-benchmarks"))] +pub const EXISTENTIAL_DEPOSIT: Balance = 0; + +/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is +/// used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); + +/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used by +/// `Operational` extrinsics. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// We allow for 0.5 of a second of compute with a 12 second average block time. +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +/// We allow for 500ms of compute with a 12 second average block time. +pub const WEIGHT_MILLISECS_PER_BLOCK: u64 = 500; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + + // This part is copied from Substrate's `bin/node/runtime/src/lib.rs`. + // The `RuntimeBlockLength` and `RuntimeBlockWeights` exist here because the + // `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize + // the lazy contract deletion. + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have some extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); + pub const SS58Prefix: u16 = 42; +} + +// Configure FRAME pallets to include in runtime. +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = IdentityLookup; + /// The index type for storing how many extrinsics an account has signed. + type Nonce = Index; + /// The index type for blocks. + type Block = Block; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// Converts a module to an index of this module in the runtime. + type PalletInfo = PalletInfo; + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// The basic call filter to use in dispatchable. + type BaseCallFilter = InsideBoth; + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = weights::frame_system::SubstrateWeight; + /// Block & extrinsics weights: base values and limits. + type BlockWeights = RuntimeBlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = RuntimeBlockLength; + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The action to take on a Runtime Upgrade + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = RuntimeTask; +} + +parameter_types! { + pub const TransactionByteFee: Balance = 1; +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // This will burn the fees + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; +} + +parameter_types! { + pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = ConstU32<50>; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = RuntimeFreezeReason; + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxHolds = ConstU32<0>; + type WeightInfo = weights::pallet_balances::SubstrateWeight; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; +pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; +pub const BLOCK_PROCESSING_VELOCITY: u32 = 1; + +type ConsensusHook = pallet_async_backing::consensus_hook::FixedVelocityConsensusHook< + Runtime, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + +impl cumulus_pallet_parachain_system::Config for Runtime { + type WeightInfo = weights::cumulus_pallet_parachain_system::SubstrateWeight; + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type OutboundXcmpMessageSource = XcmpQueue; + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type ReservedDmpWeight = ReservedDmpWeight; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; +} + +pub struct ParaSlotProvider; +impl Get<(Slot, SlotDuration)> for ParaSlotProvider { + fn get() -> (Slot, SlotDuration) { + let slot = u64::from(::SlotBeacon::slot()); + (Slot::from(slot), SlotDuration::from_millis(SLOT_DURATION)) + } +} + +parameter_types! { + pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; +} + +impl pallet_async_backing::Config for Runtime { + type AllowMultipleBlocksPerSlot = ConstBool; + type GetAndVerifySlot = + pallet_async_backing::ParaSlot; + type ExpectedBlockTime = ExpectedBlockTime; +} + +impl parachain_info::Config for Runtime {} + +parameter_types! { + pub const Period: u32 = 6 * HOURS; + pub const Offset: u32 = 0; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_sudo::SubstrateWeight; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = weights::pallet_utility::SubstrateWeight; +} + +/// The type used to represent the kinds of proxying allowed. +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, MaxEncodedLen, TypeInfo, +)] +#[allow(clippy::unnecessary_cast)] +pub enum ProxyType { + /// All calls can be proxied. This is the trivial/most permissive filter. + Any = 0, + /// Only extrinsics that do not transfer funds. + NonTransfer = 1, + /// Only extrinsics related to governance (democracy and collectives). + Governance = 2, + /// Allow to veto an announced proxy call. + CancelProxy = 3, + /// Allow extrinsic related to Balances. + Balances = 4, +} + +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + // Since proxy filters are respected in all dispatches of the Utility + // pallet, it should never need to be filtered by any proxy. + if let RuntimeCall::Utility(..) = c { + return true; + } + + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => { + matches!( + c, + RuntimeCall::System(..) + | RuntimeCall::ParachainSystem(..) + | RuntimeCall::Timestamp(..) + | RuntimeCall::Proxy(..) + ) + } + // We don't have governance yet + ProxyType::Governance => false, + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) + ), + ProxyType::Balances => { + matches!(c, RuntimeCall::Balances(..)) + } + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + _ => false, + } + } +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + // One storage item; key size 32, value size 8 + type ProxyDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 21 bytes (20 bytes AccountId + 1 byte sizeof(ProxyType)). + type ProxyDepositFactor = ConstU128<{ currency::deposit(0, 21) }>; + type MaxProxies = ConstU32<32>; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 56 bytes: + // - 20 bytes AccountId + // - 32 bytes Hasher (Blake2256) + // - 4 bytes BlockNumber (u32) + type AnnouncementDepositFactor = ConstU128<{ currency::deposit(0, 56) }>; + type WeightInfo = weights::pallet_proxy::SubstrateWeight; +} + +pub struct XcmExecutionManager; +impl xcm_primitives::PauseXcmExecution for XcmExecutionManager { + fn suspend_xcm_execution() -> DispatchResult { + XcmpQueue::suspend_xcm_execution(RuntimeOrigin::root()) + } + fn resume_xcm_execution() -> DispatchResult { + XcmpQueue::resume_xcm_execution(RuntimeOrigin::root()) + } +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MigrationsList = (migrations::TemplateMigrations,); + type XcmExecutionManager = XcmExecutionManager; +} + +/// Maintenance mode Call filter +pub struct MaintenanceFilter; +impl Contains for MaintenanceFilter { + fn contains(c: &RuntimeCall) -> bool { + !matches!( + c, + RuntimeCall::Balances(_) + | RuntimeCall::Ethereum(_) + | RuntimeCall::EVM(_) + | RuntimeCall::PolkadotXcm(_) + ) + } +} + +/// Normal Call Filter +/// We dont allow to create nor mint assets, this for now is disabled +/// We only allow transfers. For now creation of assets will go through +/// asset-manager, while minting/burning only happens through xcm messages +/// This can change in the future +pub struct NormalFilter; +impl Contains for NormalFilter { + fn contains(c: &RuntimeCall) -> bool { + !matches!( + c, + // Filtering the EVM prevents possible re-entrancy from the precompiles which could + // lead to unexpected scenarios. + // See https://github.com/PureStake/sr-moonbeam/issues/30 + // Note: It is also assumed that EVM calls are only allowed through `Origin::Root` so + // this can be seen as an additional security + RuntimeCall::EVM(_) + ) + } +} + +impl pallet_maintenance_mode::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type NormalCallFilter = NormalFilter; + type MaintenanceCallFilter = MaintenanceFilter; + type MaintenanceOrigin = EnsureRoot; + type XcmExecutionManager = XcmExecutionManager; +} + +// To match ethereum expectations +const BLOCK_GAS_LIMIT: u64 = 15_000_000; + +impl pallet_evm_chain_id::Config for Runtime {} + +pub struct FindAuthorAdapter; +impl FindAuthor for FindAuthorAdapter { + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + if let Some(author) = AuthorInherent::find_author(digests) { + return Some(H160::from_slice(&author.encode()[0..20])); + } + None + } +} + +parameter_types! { + pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); + pub PrecompilesValue: TemplatePrecompiles = TemplatePrecompiles::<_>::new(); + pub WeightPerGas: Weight = Weight::from_parts(weight_per_gas(BLOCK_GAS_LIMIT, NORMAL_DISPATCH_RATIO, WEIGHT_MILLISECS_PER_BLOCK), 0); + pub SuicideQuickClearLimit: u32 = 0; +} + +impl_on_charge_evm_transaction!(); +impl pallet_evm::Config for Runtime { + type FeeCalculator = BaseFee; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = TemplatePrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = EVMChainId; + type BlockGasLimit = BlockGasLimit; + type Runner = pallet_evm::runner::stack::Runner; + type OnChargeTransaction = OnChargeEVMTransaction<()>; + type OnCreate = (); + type FindAuthor = FindAuthorAdapter; + // TODO: update in the future + type GasLimitPovSizeRatio = (); + type SuicideQuickClearLimit = SuicideQuickClearLimit; + type Timestamp = Timestamp; + type WeightInfo = (); +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} + +parameter_types! { + pub BoundDivision: U256 = U256::from(1024); +} + +parameter_types! { + pub DefaultBaseFeePerGas: U256 = U256::from(2_000_000_000); + pub DefaultElasticity: Permill = Permill::from_parts(125_000); +} + +pub struct BaseFeeThreshold; +impl pallet_base_fee::BaseFeeThreshold for BaseFeeThreshold { + fn lower() -> Permill { + Permill::zero() + } + fn ideal() -> Permill { + Permill::from_parts(500_000) + } + fn upper() -> Permill { + Permill::from_parts(1_000_000) + } +} + +impl pallet_base_fee::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Threshold = BaseFeeThreshold; + type DefaultBaseFeePerGas = DefaultBaseFeePerGas; + type DefaultElasticity = DefaultElasticity; +} + +impl pallet_root_testing::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_tx_pause::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PauseOrigin = EnsureRoot; + type UnpauseOrigin = EnsureRoot; + type WhitelistedCalls = (); + type MaxNameLen = ConstU32<256>; + type WeightInfo = weights::pallet_tx_pause::SubstrateWeight; +} + +impl dp_impl_tanssi_pallets_config::Config for Runtime { + const SLOT_DURATION: u64 = SLOT_DURATION; + type TimestampWeights = weights::pallet_timestamp::SubstrateWeight; + type AuthorInherentWeights = weights::pallet_author_inherent::SubstrateWeight; + type AuthoritiesNotingWeights = weights::pallet_cc_authorities_noting::SubstrateWeight; +} + +parameter_types! { + // One storage item; key size 32 + 20; value is size 4+4+16+20. Total = 1 * (52 + 44) + pub const DepositBase: Balance = currency::deposit(1, 96); + // Additional storage item size of 20 bytes. + pub const DepositFactor: Balance = currency::deposit(0, 20); + pub const MaxSignatories: u32 = 100; +} + +impl pallet_multisig::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type DepositBase = DepositBase; + type DepositFactor = DepositFactor; + type MaxSignatories = MaxSignatories; + type WeightInfo = weights::pallet_multisig::SubstrateWeight; +} + +impl_tanssi_pallets_config!(Runtime); + +// Create the runtime by composing the FRAME pallets that were previously configured. +construct_runtime!( + pub enum Runtime + { + // System support stuff. + System: frame_system = 0, + ParachainSystem: cumulus_pallet_parachain_system = 1, + Timestamp: pallet_timestamp = 2, + ParachainInfo: parachain_info = 3, + Sudo: pallet_sudo = 4, + Utility: pallet_utility = 5, + Proxy: pallet_proxy = 6, + Migrations: pallet_migrations = 7, + MaintenanceMode: pallet_maintenance_mode = 8, + TxPause: pallet_tx_pause = 9, + + // Monetary stuff. + Balances: pallet_balances = 10, + + // Other utilities + Multisig: pallet_multisig = 16, + + // ContainerChain + AuthoritiesNoting: pallet_cc_authorities_noting = 50, + AuthorInherent: pallet_author_inherent = 51, + + // Frontier + Ethereum: pallet_ethereum = 60, + EVM: pallet_evm = 61, + EVMChainId: pallet_evm_chain_id = 62, + BaseFee: pallet_base_fee = 64, + TransactionPayment: pallet_transaction_payment = 66, + + // XCM + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Storage, Event} = 70, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 71, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 72, + PolkadotXcm: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 73, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 74, + ForeignAssets: pallet_assets::::{Pallet, Call, Storage, Event} = 75, + ForeignAssetsCreator: pallet_foreign_asset_creator::{Pallet, Call, Storage, Event} = 76, + AssetRate: pallet_asset_rate::{Pallet, Call, Storage, Event} = 77, + XcmExecutorUtils: pallet_xcm_executor_utils::{Pallet, Call, Storage, Event} = 78, + + RootTesting: pallet_root_testing = 100, + AsyncBacking: pallet_async_backing::{Pallet, Storage} = 110, + } +); + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_benchmarking::define_benchmarks!( + [frame_system, frame_system_benchmarking::Pallet::] + [cumulus_pallet_parachain_system, ParachainSystem] + [pallet_timestamp, Timestamp] + [pallet_sudo, Sudo] + [pallet_utility, Utility] + [pallet_proxy, Proxy] + [pallet_tx_pause, TxPause] + [pallet_balances, Balances] + [pallet_multisig, Multisig] + [pallet_cc_authorities_noting, AuthoritiesNoting] + [pallet_author_inherent, AuthorInherent] + [cumulus_pallet_xcmp_queue, XcmpQueue] + [cumulus_pallet_dmp_queue, DmpQueue] + [pallet_xcm, PalletXcmExtrinsicsBenchmark::] + [pallet_xcm_benchmarks::generic, pallet_xcm_benchmarks::generic::Pallet::] + [pallet_message_queue, MessageQueue] + [pallet_assets, ForeignAssets] + [pallet_foreign_asset_creator, ForeignAssetsCreator] + [pallet_asset_rate, AssetRate] + [pallet_xcm_executor_utils, XcmExecutorUtils] + ); +} + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + xt: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + // Filtered calls should not enter the tx pool as they'll fail if inserted. + // If this call is not allowed, we return early. + if !::BaseCallFilter::contains(&xt.0.function) { + return InvalidTransaction::Call.into(); + } + + // This runtime uses Substrate's pallet transaction payment. This + // makes the chain feel like a standard Substrate chain when submitting + // frame transactions and using Substrate ecosystem tools. It has the downside that + // transaction are not prioritized by gas_price. The following code reprioritizes + // transactions to overcome this. + // + // A more elegant, ethereum-first solution is + // a pallet that replaces pallet transaction payment, and allows users + // to directly specify a gas price rather than computing an effective one. + // #HopefullySomeday + + // First we pass the transactions to the standard FRAME executive. This calculates all the + // necessary tags, longevity and other properties that we will leave unchanged. + // This also assigns some priority that we don't care about and will overwrite next. + let mut intermediate_valid = Executive::validate_transaction(source, xt.clone(), block_hash)?; + + let dispatch_info = xt.get_dispatch_info(); + + // If this is a pallet ethereum transaction, then its priority is already set + // according to effective priority fee from pallet ethereum. If it is any other kind of + // transaction, we modify its priority. The goal is to arrive at a similar metric used + // by pallet ethereum, which means we derive a fee-per-gas from the txn's tip and + // weight. + Ok(match &xt.0.function { + RuntimeCall::Ethereum(transact { .. }) => intermediate_valid, + _ if dispatch_info.class != DispatchClass::Normal => intermediate_valid, + _ => { + let tip = match xt.0.signature { + None => 0, + Some((_, _, ref signed_extra)) => { + // Yuck, this depends on the index of charge transaction in Signed Extra + let charge_transaction = &signed_extra.7; + charge_transaction.tip() + } + }; + + let effective_gas = + ::GasWeightMapping::weight_to_gas( + dispatch_info.weight + ); + let tip_per_gas = if effective_gas > 0 { + tip.saturating_div(u128::from(effective_gas)) + } else { + 0 + }; + + // Overwrite the original prioritization with this ethereum one + intermediate_valid.priority = tip_per_gas as u64; + intermediate_valid + } + }) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl async_backing_primitives::UnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: async_backing_primitives::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn create_default_config() -> Vec { + create_default_config::() + } + + fn build_config(config: Vec) -> sp_genesis_builder::Result { + build_config::(config) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata( + extra: bool, + ) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig, + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{BenchmarkBatch, Benchmarking, BenchmarkError}; + use sp_core::storage::TrackedStorageKey; + use staging_xcm::latest::prelude::*; + impl frame_system_benchmarking::Config for Runtime { + fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { + ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); + Ok(()) + } + + fn verify_set_code() { + System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); + } + } + use xcm_config::SelfReserve; + + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + SelfReserve::get(), + ExistentialDeposit::get() + ).into()); + } + + impl pallet_xcm_benchmarks::Config for Runtime { + type XcmConfig = xcm_config::XcmConfig; + type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn valid_destination() -> Result { + Ok(MultiLocation::parent()) + } + fn worst_case_holding(_depositable_count: u32) -> MultiAssets { + // We only care for native asset until we support others + // TODO: refactor this case once other assets are supported + vec![MultiAsset{ + id: Concrete(MultiLocation::here()), + fun: Fungible(u128::MAX), + }].into() + } + } + + impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; + type RuntimeCall = RuntimeCall; + + fn worst_case_response() -> (u64, Response) { + (0u64, Response::Version(Default::default())) + } + + fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result<(MultiLocation, Junction), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { + Ok((MultiLocation::parent(), frame_system::Call::remark_with_event { remark: vec![] }.into())) + } + + fn subscribe_origin() -> Result { + Ok(MultiLocation::parent()) + } + + fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError> { + let origin = MultiLocation::parent(); + let assets: MultiAssets = (Concrete(MultiLocation::parent()), 1_000u128).into(); + let ticket = MultiLocation { parents: 0, interior: Here }; + Ok((origin, ticket, assets)) + } + + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn alias_origin() -> Result<(MultiLocation, MultiLocation), BenchmarkError> { + Err(BenchmarkError::Skip) + } + } + + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; + impl pallet_xcm::benchmarking::Config for Runtime { + fn reachable_dest() -> Option { + Some(Parent.into()) + } + + fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> { + // Relay/native token can be teleported between AH and Relay. + Some(( + MultiAsset { + fun: Fungible(EXISTENTIAL_DEPOSIT), + id: Concrete(Parent.into()) + }, + Parent.into(), + )) + } + + fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> { + use xcm_config::SelfReserve; + // AH can reserve transfer native token to some random parachain. + let random_para_id = 43211234; + let balance = EXISTENTIAL_DEPOSIT * 10; + + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( + random_para_id.into() + ); + Some(( + MultiAsset { + fun: Fungible(balance), + id: Concrete(SelfReserve::get()) + }, + ParentThen(Parachain(random_para_id).into()).into(), + )) + } + + fn set_up_complex_asset_transfer( + ) -> Option<(MultiAssets, u32, MultiLocation, Box)> { + use xcm_config::SelfReserve; + // Transfer to Relay some local AH asset (local-reserve-transfer) while paying + // fees using teleported native token. + // (We don't care that Relay doesn't accept incoming unknown AH local asset) + let dest = Parent.into(); + + let fee_amount = EXISTENTIAL_DEPOSIT; + let fee_asset: MultiAsset = (SelfReserve::get(), fee_amount).into(); + + let who = frame_benchmarking::whitelisted_caller(); + // Give some multiple of the existential deposit + let balance = fee_amount + EXISTENTIAL_DEPOSIT * 1000; + let _ = >::make_free_balance_be( + &who, balance, + ); + + // verify initial balance + assert_eq!(Balances::free_balance(&who), balance); + + // set up local asset + let asset_amount = 10u128; + let initial_asset_amount = asset_amount * 10; + + let (asset_id, asset_location) = pallet_foreign_asset_creator::benchmarks::create_default_minted_asset::( + initial_asset_amount, + who + ); + + let transfer_asset: MultiAsset = (asset_location, asset_amount).into(); + + let assets: MultiAssets = vec![fee_asset.clone(), transfer_asset].into(); + let fee_index = if assets.get(0).unwrap().eq(&fee_asset) { 0 } else { 1 }; + + // verify transferred successfully + let verify = Box::new(move || { + // verify native balance after transfer, decreased by transferred fee amount + // (plus transport fees) + assert!(Balances::free_balance(&who) <= balance - fee_amount); + // verify asset balance decreased by exactly transferred amount + assert_eq!( + ForeignAssets::balance(asset_id, &who), + initial_asset_amount - asset_amount, + ); + }); + Some((assets, fee_index as u32, dest, verify)) + } + } + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac") + .to_vec() + .into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80") + .to_vec() + .into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a") + .to_vec() + .into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850") + .to_vec() + .into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7") + .to_vec() + .into(), + // The transactional storage limit. + hex_literal::hex!("3a7472616e73616374696f6e5f6c6576656c3a") + .to_vec() + .into(), + + // ParachainInfo ParachainId + hex_literal::hex!( "0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f") + .to_vec() + .into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmarks!(params, batches); + + Ok(batches) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + let weight = Executive::try_runtime_upgrade(checks).unwrap(); + (weight, RuntimeBlockWeights::get().max_block) + } + + fn execute_block( + block: Block, + state_root_check: bool, + signature_check: bool, + select: frame_try_runtime::TryStateSelect, + ) -> Weight { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. + Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap() + } + } + + impl fp_rpc::EthereumRuntimeRPCApi for Runtime { + fn chain_id() -> u64 { + ::ChainId::get() + } + + fn account_basic(address: H160) -> EVMAccount { + let (account, _) = pallet_evm::Pallet::::account_basic(&address); + account + } + + fn gas_price() -> U256 { + let (gas_price, _) = ::FeeCalculator::min_gas_price(); + gas_price + } + + fn account_code_at(address: H160) -> Vec { + pallet_evm::AccountCodes::::get(address) + } + + fn author() -> H160 { + >::find_author() + } + + fn storage_at(address: H160, index: U256) -> H256 { + let mut tmp = [0u8; 32]; + index.to_big_endian(&mut tmp); + pallet_evm::AccountStorages::::get(address, H256::from_slice(&tmp[..])) + } + + fn call( + from: H160, + to: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + _estimate: bool, + access_list: Option)>>, + ) -> Result { + let is_transactional = false; + let validate = true; + ::Runner::call( + from, + to, + data, + value, + gas_limit.min(u64::MAX.into()).low_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list.unwrap_or_default(), + is_transactional, + validate, + None, + None, + ::config(), + ).map_err(|err| err.error.into()) + } + + fn create( + from: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + _estimate: bool, + access_list: Option)>>, + ) -> Result { + let is_transactional = false; + let validate = true; + ::Runner::create( + from, + data, + value, + gas_limit.min(u64::MAX.into()).low_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list.unwrap_or_default(), + is_transactional, + validate, + None, + None, + ::config(), + ).map_err(|err| err.error.into()) + } + + fn current_transaction_statuses() -> Option> { + pallet_ethereum::CurrentTransactionStatuses::::get() + } + + fn current_block() -> Option { + pallet_ethereum::CurrentBlock::::get() + } + + fn current_receipts() -> Option> { + pallet_ethereum::CurrentReceipts::::get() + } + + fn current_all() -> ( + Option, + Option>, + Option>, + ) { + ( + pallet_ethereum::CurrentBlock::::get(), + pallet_ethereum::CurrentReceipts::::get(), + pallet_ethereum::CurrentTransactionStatuses::::get() + ) + } + + fn extrinsic_filter( + xts: Vec<::Extrinsic>, + ) -> Vec { + xts.into_iter().filter_map(|xt| match xt.0.function { + RuntimeCall::Ethereum(transact { transaction }) => Some(transaction), + _ => None + }).collect::>() + } + + fn elasticity() -> Option { + Some(pallet_base_fee::Elasticity::::get()) + } + + fn gas_limit_multiplier_support() {} + + fn pending_block(xts: Vec<::Extrinsic>) -> (Option, Option>) { + for ext in xts.into_iter() { + let _ = Executive::apply_extrinsic(ext); + } + + Ethereum::on_finalize(System::block_number() + 1); + + ( + pallet_ethereum::CurrentBlock::::get(), + pallet_ethereum::CurrentTransactionStatuses::::get() + ) + } + } + + impl fp_rpc::ConvertTransactionRuntimeApi for Runtime { + fn convert_transaction( + transaction: pallet_ethereum::Transaction + ) -> ::Extrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl dp_slot_duration_runtime_api::TanssiSlotDurationApi for Runtime { + fn slot_duration() -> u64 { + SLOT_DURATION + } + } +} + +#[allow(dead_code)] +struct CheckInherents; + +// TODO: this should be removed but currently if we remove it the relay does not check anything +// related to other inherents that are not parachain-system +#[allow(deprecated)] +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + CheckInherents = CheckInherents, + BlockExecutor = pallet_author_inherent::BlockExecutor::, +} diff --git a/container-chains/runtime-templates/frontier/src/migrations.rs b/container-chains/runtime-templates/frontier/src/migrations.rs new file mode 100644 index 0000000..cd50a67 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/migrations.rs @@ -0,0 +1,62 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Migrations +//! +//! This module acts as a registry where each migration is defined. Each migration should implement +//! the "Migration" trait declared in the pallet-migrations crate. + +use { + frame_support::{pallet_prelude::GetStorageVersion, traits::PalletInfoAccess}, + pallet_migrations::{GetMigrations, Migration}, + runtime_common::migrations::{ + PolkadotXcmMigrationFixVersion, XcmpQueueMigrationFixVersion, XcmpQueueMigrationV3, + XcmpQueueMigrationV4, + }, + sp_std::{marker::PhantomData, prelude::*}, +}; + +pub struct TemplateMigrations( + PhantomData<(Runtime, XcmpQueue, PolkadotXcm)>, +); + +impl GetMigrations + for TemplateMigrations +where + PolkadotXcm: GetStorageVersion + PalletInfoAccess + 'static, + XcmpQueue: GetStorageVersion + PalletInfoAccess + 'static, + Runtime: pallet_evm::Config, + Runtime: frame_system::Config, + Runtime: cumulus_pallet_xcmp_queue::Config, +{ + fn get_migrations() -> Vec> { + // let migrate_precompiles = MigratePrecompileDummyCode::(Default::default()); + let migrate_polkadot_xcm_v1 = + PolkadotXcmMigrationFixVersion::(Default::default()); + let migrate_xcmp_queue_v2 = + XcmpQueueMigrationFixVersion::(Default::default()); + let migrate_xcmp_queue_v3 = XcmpQueueMigrationV3::(Default::default()); + let migrate_xcmp_queue_v4 = XcmpQueueMigrationV4::(Default::default()); + vec![ + // Applied in runtime 400 + // Box::new(migrate_precompiles), + Box::new(migrate_polkadot_xcm_v1), + Box::new(migrate_xcmp_queue_v2), + Box::new(migrate_xcmp_queue_v3), + Box::new(migrate_xcmp_queue_v4), + ] + } +} diff --git a/container-chains/runtime-templates/frontier/src/precompiles.rs b/container-chains/runtime-templates/frontier/src/precompiles.rs new file mode 100644 index 0000000..bdc3f66 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/precompiles.rs @@ -0,0 +1,113 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::xcm_config::{ForeignAssetsInstance, XcmConfig}, + frame_support::parameter_types, + pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata}, + pallet_evm_precompile_batch::BatchPrecompile, + pallet_evm_precompile_call_permit::CallPermitPrecompile, + pallet_evm_precompile_modexp::Modexp, + pallet_evm_precompile_sha3fips::Sha3FIPS256, + pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}, + pallet_evm_precompile_xcm_utils::{AllExceptXcmExecute, XcmUtilsPrecompile}, + pallet_evm_precompileset_assets_erc20::Erc20AssetsPrecompileSet, + precompile_utils::precompile_set::{ + AcceptDelegateCall, AddressU64, CallableByContract, CallableByPrecompile, PrecompileAt, + PrecompileSetBuilder, PrecompileSetStartingWith, PrecompilesInRangeInclusive, + SubcallWithMaxNesting, + }, +}; + +/// ERC20 metadata for the native token. +pub struct NativeErc20Metadata; + +impl Erc20Metadata for NativeErc20Metadata { + /// Returns the name of the token. + fn name() -> &'static str { + "UNIT token" + } + + /// Returns the symbol of the token. + fn symbol() -> &'static str { + "UNIT" + } + + /// Returns the decimals places of the token. + fn decimals() -> u8 { + 18 + } + + /// Must return `true` only if it represents the main native currency of + /// the network. It must be the currency used in `pallet_evm`. + fn is_native_currency() -> bool { + true + } +} + +/// The asset precompile address prefix. Addresses that match against this prefix will be routed +/// to Erc20AssetsPrecompileSet being marked as foreign +pub const FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[255u8; 18]; + +parameter_types! { + pub ForeignAssetPrefix: &'static [u8] = FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX; +} + +type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile); + +#[precompile_utils::precompile_name_from_address] +type TemplatePrecompilesAt = ( + // Ethereum precompiles: + // Allow DELEGATECALL to stay compliant with Ethereum behavior. + PrecompileAt, ECRecover, EthereumPrecompilesChecks>, + PrecompileAt, Sha256, EthereumPrecompilesChecks>, + PrecompileAt, Ripemd160, EthereumPrecompilesChecks>, + PrecompileAt, Identity, EthereumPrecompilesChecks>, + PrecompileAt, Modexp, EthereumPrecompilesChecks>, + // Non-template specific nor Ethereum precompiles : + PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>, + PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>, + // Template specific precompiles: + PrecompileAt< + AddressU64<2048>, + Erc20BalancesPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt, BatchPrecompile, SubcallWithMaxNesting<2>>, + PrecompileAt< + AddressU64<2050>, + CallPermitPrecompile, + (SubcallWithMaxNesting<0>, CallableByContract), + >, + PrecompileAt< + AddressU64<2051>, + XcmUtilsPrecompile, + CallableByContract>, + >, +); + +pub type TemplatePrecompiles = PrecompileSetBuilder< + R, + ( + PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), TemplatePrecompilesAt>, + // Prefixed precompile sets (XC20) + PrecompileSetStartingWith< + ForeignAssetPrefix, + Erc20AssetsPrecompileSet, + (CallableByContract, CallableByPrecompile), + >, + ), +>; diff --git a/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_dmp_queue.rs b/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_dmp_queue.rs new file mode 100644 index 0000000..55ded56 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_dmp_queue.rs @@ -0,0 +1,133 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for cumulus_pallet_dmp_queue +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// cumulus_pallet_dmp_queue +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/cumulus_pallet_dmp_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for cumulus_pallet_dmp_queue using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl cumulus_pallet_dmp_queue::WeightInfo for SubstrateWeight { + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn on_idle_good_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65661` + // Estimated: `69126` + // Minimum execution time: 125_583_000 picoseconds. + Weight::from_parts(127_942_000, 69126) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + fn on_idle_large_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65660` + // Estimated: `69125` + // Minimum execution time: 74_056_000 picoseconds. + Weight::from_parts(75_026_000, 69125) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn on_idle_overweight_good_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65691` + // Estimated: `69156` + // Minimum execution time: 119_948_000 picoseconds. + Weight::from_parts(122_167_000, 69156) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + fn on_idle_overweight_large_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65690` + // Estimated: `69155` + // Minimum execution time: 68_728_000 picoseconds. + Weight::from_parts(69_890_000, 69155) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_parachain_system.rs b/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_parachain_system.rs new file mode 100644 index 0000000..df867e7 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_parachain_system.rs @@ -0,0 +1,80 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for cumulus_pallet_parachain_system +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// cumulus_pallet_parachain_system +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/cumulus_pallet_parachain_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for cumulus_pallet_parachain_system using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl cumulus_pallet_parachain_system::WeightInfo for SubstrateWeight { + /// Storage: `ParachainSystem::LastDmqMqcHead` (r:1 w:1) + /// Proof: `ParachainSystem::LastDmqMqcHead` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ProcessedDownwardMessages` (r:0 w:1) + /// Proof: `ParachainSystem::ProcessedDownwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1000) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 1000]`. + fn enqueue_inbound_downward_messages(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `12` + // Estimated: `3517` + // Minimum execution time: 2_146_000 picoseconds. + Weight::from_parts(2_181_000, 3517) + // Standard Error: 35_149 + .saturating_add(Weight::from_parts(194_124_508, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_xcmp_queue.rs b/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_xcmp_queue.rs new file mode 100644 index 0000000..383fbcf --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/cumulus_pallet_xcmp_queue.rs @@ -0,0 +1,152 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for cumulus_pallet_xcmp_queue +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// cumulus_pallet_xcmp_queue +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/cumulus_pallet_xcmp_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for cumulus_pallet_xcmp_queue using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl cumulus_pallet_xcmp_queue::WeightInfo for SubstrateWeight { + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:1) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_config_with_u32() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `1594` + // Minimum execution time: 5_665_000 picoseconds. + Weight::from_parts(5_873_000, 1594) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:0) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn enqueue_xcmp_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `115` + // Estimated: `3517` + // Minimum execution time: 14_867_000 picoseconds. + Weight::from_parts(15_236_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn suspend_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `1594` + // Minimum execution time: 3_108_000 picoseconds. + Weight::from_parts(3_280_000, 1594) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn resume_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `1629` + // Minimum execution time: 4_137_000 picoseconds. + Weight::from_parts(4_283_000, 1629) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn take_first_concatenated_xcm() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 55_147_000 picoseconds. + Weight::from_parts(55_408_000, 0) + } + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:0) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn on_idle_good_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65744` + // Estimated: `69209` + // Minimum execution time: 116_672_000 picoseconds. + Weight::from_parts(118_871_000, 69209) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + fn on_idle_large_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65743` + // Estimated: `69208` + // Minimum execution time: 58_460_000 picoseconds. + Weight::from_parts(59_520_000, 69208) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/frame_system.rs b/container-chains/runtime-templates/frontier/src/weights/frame_system.rs new file mode 100644 index 0000000..abf9337 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/frame_system.rs @@ -0,0 +1,185 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for frame_system +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// frame_system +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/frame_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for frame_system using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl frame_system::WeightInfo for SubstrateWeight { + /// The range of component `b` is `[0, 3932160]`. + fn remark(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_720_000 picoseconds. + Weight::from_parts(1_269_593, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(433, 0).saturating_mul(b.into())) + } + /// The range of component `b` is `[0, 3932160]`. + fn remark_with_event(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_530_000 picoseconds. + Weight::from_parts(7_801_000, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_778, 0).saturating_mul(b.into())) + } + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + fn set_heap_pages() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1485` + // Minimum execution time: 4_702_000 picoseconds. + Weight::from_parts(4_935_000, 1485) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `127` + // Estimated: `1612` + // Minimum execution time: 145_799_273_000 picoseconds. + Weight::from_parts(147_924_130_000, 1612) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn set_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_786_000 picoseconds. + Weight::from_parts(2_900_000, 0) + // Standard Error: 2_302 + .saturating_add(Weight::from_parts(914_245, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn kill_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_889_000 picoseconds. + Weight::from_parts(2_984_000, 0) + // Standard Error: 963 + .saturating_add(Weight::from_parts(642_377, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `p` is `[0, 1000]`. + fn kill_prefix(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `74 + p * (69 ±0)` + // Estimated: `84 + p * (70 ±0)` + // Minimum execution time: 5_194_000 picoseconds. + Weight::from_parts(5_299_000, 84) + // Standard Error: 2_038 + .saturating_add(Weight::from_parts(1_206_738, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) + } + /// Storage: `System::AuthorizedUpgrade` (r:0 w:1) + /// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + fn authorize_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 23_274_000 picoseconds. + Weight::from_parts(25_517_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::AuthorizedUpgrade` (r:1 w:1) + /// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn apply_authorized_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `149` + // Estimated: `1634` + // Minimum execution time: 151_338_478_000 picoseconds. + Weight::from_parts(153_336_555_000, 1634) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/mod.rs b/container-chains/runtime-templates/frontier/src/weights/mod.rs new file mode 100644 index 0000000..3bc8f23 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/mod.rs @@ -0,0 +1,39 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! A list of the different weight modules for our runtime. + +pub mod cumulus_pallet_dmp_queue; +pub mod cumulus_pallet_parachain_system; +pub mod cumulus_pallet_xcmp_queue; +pub mod frame_system; +pub mod pallet_asset_rate; +pub mod pallet_assets; +pub mod pallet_author_inherent; +pub mod pallet_balances; +pub mod pallet_cc_authorities_noting; +pub mod pallet_foreign_asset_creator; +pub mod pallet_message_queue; +pub mod pallet_multisig; +pub mod pallet_proxy; + +pub mod pallet_sudo; +pub mod pallet_timestamp; +pub mod pallet_tx_pause; +pub mod pallet_utility; +pub mod pallet_xcm; +pub mod pallet_xcm_executor_utils; +pub mod xcm; diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_asset_rate.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_asset_rate.rs new file mode 100644 index 0000000..06a83e2 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_asset_rate.rs @@ -0,0 +1,90 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_asset_rate +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_asset_rate +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_asset_rate.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_asset_rate using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_asset_rate::WeightInfo for SubstrateWeight { + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(34), added: 2509, mode: `MaxEncodedLen`) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3499` + // Minimum execution time: 12_427_000 picoseconds. + Weight::from_parts(12_705_000, 3499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(34), added: 2509, mode: `MaxEncodedLen`) + fn update() -> Weight { + // Proof Size summary in bytes: + // Measured: `135` + // Estimated: `3499` + // Minimum execution time: 12_480_000 picoseconds. + Weight::from_parts(12_726_000, 3499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(34), added: 2509, mode: `MaxEncodedLen`) + fn remove() -> Weight { + // Proof Size summary in bytes: + // Measured: `135` + // Estimated: `3499` + // Minimum execution time: 13_196_000 picoseconds. + Weight::from_parts(13_474_000, 3499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_assets.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_assets.rs new file mode 100644 index 0000000..7c98788 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_assets.rs @@ -0,0 +1,489 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_assets +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_assets.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_assets::WeightInfo for SubstrateWeight { + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(0, 0) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn force_create() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3625` + // Minimum execution time: 11_887_000 picoseconds. + Weight::from_parts(12_178_000, 3625) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn start_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `225` + // Estimated: `3625` + // Minimum execution time: 12_827_000 picoseconds. + Weight::from_parts(13_164_000, 3625) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1001 w:1000) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1000 w:1000) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn destroy_accounts(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `114 + c * (183 ±0)` + // Estimated: `3625 + c * (2591 ±0)` + // Minimum execution time: 17_120_000 picoseconds. + Weight::from_parts(17_352_000, 3625) + // Standard Error: 17_600 + .saturating_add(Weight::from_parts(14_849_061, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(c.into())) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1001 w:1000) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 1000]`. + fn destroy_approvals(a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `370 + a * (74 ±0)` + // Estimated: `3625 + a * (2597 ±0)` + // Minimum execution time: 18_528_000 picoseconds. + Weight::from_parts(18_693_000, 3625) + // Standard Error: 3_650 + .saturating_add(Weight::from_parts(5_522_658, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(a.into())) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:0) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn finish_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 13_728_000 picoseconds. + Weight::from_parts(13_946_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 25_570_000 picoseconds. + Weight::from_parts(26_170_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + fn burn() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `3625` + // Minimum execution time: 34_485_000 picoseconds. + Weight::from_parts(35_118_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `376` + // Estimated: `6156` + // Minimum execution time: 48_951_000 picoseconds. + Weight::from_parts(49_958_000, 6156) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `376` + // Estimated: `6156` + // Minimum execution time: 43_375_000 picoseconds. + Weight::from_parts(44_254_000, 6156) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `376` + // Estimated: `6156` + // Minimum execution time: 49_553_000 picoseconds. + Weight::from_parts(50_087_000, 6156) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + fn freeze() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `3625` + // Minimum execution time: 17_067_000 picoseconds. + Weight::from_parts(17_411_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + fn thaw() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `3625` + // Minimum execution time: 16_924_000 picoseconds. + Weight::from_parts(17_493_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn freeze_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `225` + // Estimated: `3625` + // Minimum execution time: 12_318_000 picoseconds. + Weight::from_parts(12_702_000, 3625) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn thaw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `225` + // Estimated: `3625` + // Minimum execution time: 12_483_000 picoseconds. + Weight::from_parts(12_836_000, 3625) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:0) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn transfer_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 14_180_000 picoseconds. + Weight::from_parts(14_542_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn set_team() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 12_353_000 picoseconds. + Weight::from_parts(12_755_000, 3625) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 15_648_000 picoseconds. + Weight::from_parts(16_160_261, 3625) + // Standard Error: 338 + .saturating_add(Weight::from_parts(1_975, 0).saturating_mul(n.into())) + // Standard Error: 338 + .saturating_add(Weight::from_parts(2_391, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `3625` + // Minimum execution time: 15_978_000 picoseconds. + Weight::from_parts(16_327_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn force_set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `78` + // Estimated: `3625` + // Minimum execution time: 13_541_000 picoseconds. + Weight::from_parts(14_126_720, 3625) + // Standard Error: 293 + .saturating_add(Weight::from_parts(911, 0).saturating_mul(n.into())) + // Standard Error: 293 + .saturating_add(Weight::from_parts(1_803, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn force_clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `3625` + // Minimum execution time: 15_601_000 picoseconds. + Weight::from_parts(16_016_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn force_asset_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 11_833_000 picoseconds. + Weight::from_parts(12_161_000, 3625) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn approve_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `225` + // Estimated: `3625` + // Minimum execution time: 20_390_000 picoseconds. + Weight::from_parts(20_853_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `429` + // Estimated: `6156` + // Minimum execution time: 58_486_000 picoseconds. + Weight::from_parts(59_395_000, 6156) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `369` + // Estimated: `3625` + // Minimum execution time: 22_162_000 picoseconds. + Weight::from_parts(22_659_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn force_cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `369` + // Estimated: `3625` + // Minimum execution time: 22_553_000 picoseconds. + Weight::from_parts(22_992_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 13_543_000 picoseconds. + Weight::from_parts(13_896_000, 3625) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 19_073_000 picoseconds. + Weight::from_parts(19_772_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3625` + // Minimum execution time: 19_114_000 picoseconds. + Weight::from_parts(19_596_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `303` + // Estimated: `3625` + // Minimum execution time: 17_323_000 picoseconds. + Weight::from_parts(17_592_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `323` + // Estimated: `3625` + // Minimum execution time: 17_204_000 picoseconds. + Weight::from_parts(17_458_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `3625` + // Minimum execution time: 16_920_000 picoseconds. + Weight::from_parts(17_206_000, 3625) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_author_inherent.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_author_inherent.rs new file mode 100644 index 0000000..479e425 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_author_inherent.rs @@ -0,0 +1,74 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_author_inherent +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_author_inherent +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_author_inherent.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_author_inherent using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_author_inherent::WeightInfo for SubstrateWeight { + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorInherent::Author` (r:1 w:0) + /// Proof: `AuthorInherent::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `AuthoritiesNoting::Authorities` (r:1 w:0) + /// Proof: `AuthoritiesNoting::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorInherent::InherentIncluded` (r:0 w:1) + /// Proof: `AuthorInherent::InherentIncluded` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + fn kick_off_authorship_validation() -> Weight { + // Proof Size summary in bytes: + // Measured: `187` + // Estimated: `1672` + // Minimum execution time: 12_457_000 picoseconds. + Weight::from_parts(12_761_000, 1672) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_balances.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_balances.rs new file mode 100644 index 0000000..d8ffa77 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_balances.rs @@ -0,0 +1,149 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_balances +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_balances +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_balances.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_balances using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_balances::WeightInfo for SubstrateWeight { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `39` + // Estimated: `3581` + // Minimum execution time: 64_977_000 picoseconds. + Weight::from_parts(66_542_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `39` + // Estimated: `3581` + // Minimum execution time: 52_023_000 picoseconds. + Weight::from_parts(53_034_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_set_balance_creating() -> Weight { + // Proof Size summary in bytes: + // Measured: `162` + // Estimated: `3581` + // Minimum execution time: 19_456_000 picoseconds. + Weight::from_parts(19_891_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_set_balance_killing() -> Weight { + // Proof Size summary in bytes: + // Measured: `162` + // Estimated: `3581` + // Minimum execution time: 26_159_000 picoseconds. + Weight::from_parts(26_725_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `201` + // Estimated: `6172` + // Minimum execution time: 67_402_000 picoseconds. + Weight::from_parts(68_888_000, 6172) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `39` + // Estimated: `3581` + // Minimum execution time: 64_503_000 picoseconds. + Weight::from_parts(65_929_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_unreserve() -> Weight { + // Proof Size summary in bytes: + // Measured: `162` + // Estimated: `3581` + // Minimum execution time: 23_262_000 picoseconds. + Weight::from_parts(23_837_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + u * (124 ±0)` + // Estimated: `990 + u * (2591 ±0)` + // Minimum execution time: 21_520_000 picoseconds. + Weight::from_parts(21_661_000, 990) + // Standard Error: 13_235 + .saturating_add(Weight::from_parts(17_507_362, 0).saturating_mul(u.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(u.into())) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_cc_authorities_noting.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_cc_authorities_noting.rs new file mode 100644 index 0000000..9b813f0 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_cc_authorities_noting.rs @@ -0,0 +1,97 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_cc_authorities_noting +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_cc_authorities_noting +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_cc_authorities_noting.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_cc_authorities_noting using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_cc_authorities_noting::WeightInfo for SubstrateWeight { + /// Storage: `AuthoritiesNoting::DidSetOrchestratorAuthorityData` (r:1 w:1) + /// Proof: `AuthoritiesNoting::DidSetOrchestratorAuthorityData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthoritiesNoting::OrchestratorParaId` (r:1 w:0) + /// Proof: `AuthoritiesNoting::OrchestratorParaId` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthoritiesNoting::Authorities` (r:0 w:1) + /// Proof: `AuthoritiesNoting::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_latest_authorities_data() -> Weight { + // Proof Size summary in bytes: + // Measured: `141` + // Estimated: `1626` + // Minimum execution time: 27_869_000 picoseconds. + Weight::from_parts(28_360_000, 1626) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `AuthoritiesNoting::Authorities` (r:0 w:1) + /// Proof: `AuthoritiesNoting::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 10]`. + fn set_authorities(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_965_000 picoseconds. + Weight::from_parts(7_556_746, 0) + // Standard Error: 2_217 + .saturating_add(Weight::from_parts(51_042, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AuthoritiesNoting::OrchestratorParaId` (r:0 w:1) + /// Proof: `AuthoritiesNoting::OrchestratorParaId` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_orchestrator_para_id() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_691_000 picoseconds. + Weight::from_parts(6_918_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_foreign_asset_creator.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_foreign_asset_creator.rs new file mode 100644 index 0000000..d29a9fd --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_foreign_asset_creator.rs @@ -0,0 +1,129 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_foreign_asset_creator +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_foreign_asset_creator +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_foreign_asset_creator.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_foreign_asset_creator using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_foreign_asset_creator::WeightInfo for SubstrateWeight { + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `EVM::Suicided` (r:1 w:0) + /// Proof: `EVM::Suicided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `EVM::AccountCodes` (r:1 w:1) + /// Proof: `EVM::AccountCodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `EVM::AccountCodesMetadata` (r:0 w:1) + /// Proof: `EVM::AccountCodesMetadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:1) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn create_foreign_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `532` + // Estimated: `3997` + // Minimum execution time: 44_763_000 picoseconds. + Weight::from_parts(45_789_000, 3997) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:2) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn change_existing_asset_type() -> Weight { + // Proof Size summary in bytes: + // Measured: `189` + // Estimated: `3654` + // Minimum execution time: 19_466_000 picoseconds. + Weight::from_parts(19_902_000, 3654) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:1) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn remove_existing_asset_type() -> Weight { + // Proof Size summary in bytes: + // Measured: `189` + // Estimated: `3654` + // Minimum execution time: 16_784_000 picoseconds. + Weight::from_parts(17_245_000, 3654) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `EVM::AccountCodes` (r:1 w:1) + /// Proof: `EVM::AccountCodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `EVM::AccountCodesMetadata` (r:0 w:1) + /// Proof: `EVM::AccountCodesMetadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `EVM::Suicided` (r:0 w:1) + /// Proof: `EVM::Suicided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:1) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn destroy_foreign_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `925` + // Estimated: `4390` + // Minimum execution time: 45_527_000 picoseconds. + Weight::from_parts(46_522_000, 4390) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_message_queue.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_message_queue.rs new file mode 100644 index 0000000..a169495 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_message_queue.rs @@ -0,0 +1,186 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_message_queue +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_message_queue +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_message_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_message_queue using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_message_queue::WeightInfo for SubstrateWeight { + /// Storage: `MessageQueue::ServiceHead` (r:1 w:0) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn ready_ring_knit() -> Weight { + // Proof Size summary in bytes: + // Measured: `223` + // Estimated: `6044` + // Minimum execution time: 13_727_000 picoseconds. + Weight::from_parts(14_217_000, 6044) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + fn ready_ring_unknit() -> Weight { + // Proof Size summary in bytes: + // Measured: `218` + // Estimated: `6044` + // Minimum execution time: 11_943_000 picoseconds. + Weight::from_parts(12_228_000, 6044) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn service_queue_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `48` + // Estimated: `3517` + // Minimum execution time: 6_765_000 picoseconds. + Weight::from_parts(7_070_000, 3517) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn service_page_base_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `72` + // Estimated: `69050` + // Minimum execution time: 6_837_000 picoseconds. + Weight::from_parts(7_070_000, 69050) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn service_page_base_no_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `72` + // Estimated: `69050` + // Minimum execution time: 7_016_000 picoseconds. + Weight::from_parts(7_262_000, 69050) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:0 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn service_page_item() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 163_675_000 picoseconds. + Weight::from_parts(166_474_000, 0) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn bump_service_head() -> Weight { + // Proof Size summary in bytes: + // Measured: `171` + // Estimated: `3517` + // Minimum execution time: 7_336_000 picoseconds. + Weight::from_parts(7_637_000, 3517) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn reap_page() -> Weight { + // Proof Size summary in bytes: + // Measured: `65667` + // Estimated: `69050` + // Minimum execution time: 61_534_000 picoseconds. + Weight::from_parts(63_398_000, 69050) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn execute_overweight_page_removed() -> Weight { + // Proof Size summary in bytes: + // Measured: `65709` + // Estimated: `69050` + // Minimum execution time: 86_548_000 picoseconds. + Weight::from_parts(87_702_000, 69050) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn execute_overweight_page_updated() -> Weight { + // Proof Size summary in bytes: + // Measured: `65709` + // Estimated: `69050` + // Minimum execution time: 121_205_000 picoseconds. + Weight::from_parts(122_592_000, 69050) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_multisig.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_multisig.rs new file mode 100644 index 0000000..66eb839 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_multisig.rs @@ -0,0 +1,172 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_multisig +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_multisig +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_multisig.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_multisig using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_multisig::WeightInfo for SubstrateWeight { + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `z` is `[0, 10000]`. + fn as_multi_threshold_1(z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 22_206_000 picoseconds. + Weight::from_parts(23_471_073, 3997) + // Standard Error: 5 + .saturating_add(Weight::from_parts(560, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_create(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `213` + // Estimated: `5587` + // Minimum execution time: 49_669_000 picoseconds. + Weight::from_parts(39_795_684, 5587) + // Standard Error: 1_213 + .saturating_add(Weight::from_parts(114_077, 0).saturating_mul(s.into())) + // Standard Error: 11 + .saturating_add(Weight::from_parts(1_486, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[3, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_approve(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `279` + // Estimated: `5587` + // Minimum execution time: 30_258_000 picoseconds. + Weight::from_parts(21_723_795, 5587) + // Standard Error: 556 + .saturating_add(Weight::from_parts(94_241, 0).saturating_mul(s.into())) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_446, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_complete(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `390 + s * (20 ±0)` + // Estimated: `5587 + s * (21 ±0)` + // Minimum execution time: 63_483_000 picoseconds. + Weight::from_parts(49_349_385, 5587) + // Standard Error: 952 + .saturating_add(Weight::from_parts(166_842, 0).saturating_mul(s.into())) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_552, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 21).saturating_mul(s.into())) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn approve_as_multi_create(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `215` + // Estimated: `5587` + // Minimum execution time: 36_363_000 picoseconds. + Weight::from_parts(37_658_602, 5587) + // Standard Error: 798 + .saturating_add(Weight::from_parts(119_193, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn approve_as_multi_approve(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `279` + // Estimated: `5587` + // Minimum execution time: 19_474_000 picoseconds. + Weight::from_parts(19_784_741, 5587) + // Standard Error: 572 + .saturating_add(Weight::from_parts(97_184, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn cancel_as_multi(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `385` + // Estimated: `5587` + // Minimum execution time: 38_333_000 picoseconds. + Weight::from_parts(39_034_442, 5587) + // Standard Error: 740 + .saturating_add(Weight::from_parts(110_636, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_proxy.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_proxy.rs new file mode 100644 index 0000000..f8790d3 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_proxy.rs @@ -0,0 +1,227 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_proxy +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_proxy +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_proxy.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_proxy using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_proxy::WeightInfo for SubstrateWeight { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `161 + p * (25 ±0)` + // Estimated: `4310 + p * (25 ±0)` + // Minimum execution time: 22_254_000 picoseconds. + Weight::from_parts(23_010_468, 4310) + // Standard Error: 1_458 + .saturating_add(Weight::from_parts(40_432, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(0, 25).saturating_mul(p.into())) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `412 + a * (56 ±0) + p * (25 ±0)` + // Estimated: `5302 + a * (57 ±0) + p * (25 ±0)` + // Minimum execution time: 50_859_000 picoseconds. + Weight::from_parts(51_538_651, 5302) + // Standard Error: 2_634 + .saturating_add(Weight::from_parts(179_993, 0).saturating_mul(a.into())) + // Standard Error: 2_722 + .saturating_add(Weight::from_parts(32_071, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 57).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 25).saturating_mul(p.into())) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, _p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `294 + a * (56 ±0)` + // Estimated: `5302` + // Minimum execution time: 27_927_000 picoseconds. + Weight::from_parts(29_013_118, 5302) + // Standard Error: 1_784 + .saturating_add(Weight::from_parts(171_679, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, _p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `294 + a * (56 ±0)` + // Estimated: `5302` + // Minimum execution time: 28_286_000 picoseconds. + Weight::from_parts(28_982_903, 5302) + // Standard Error: 1_836 + .saturating_add(Weight::from_parts(172_607, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `310 + a * (56 ±0) + p * (25 ±0)` + // Estimated: `5302` + // Minimum execution time: 37_097_000 picoseconds. + Weight::from_parts(37_102_077, 5302) + // Standard Error: 2_143 + .saturating_add(Weight::from_parts(178_297, 0).saturating_mul(a.into())) + // Standard Error: 2_214 + .saturating_add(Weight::from_parts(22_379, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `115 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 28_152_000 picoseconds. + Weight::from_parts(28_993_831, 4310) + // Standard Error: 1_154 + .saturating_add(Weight::from_parts(36_008, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `115 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 28_296_000 picoseconds. + Weight::from_parts(29_443_967, 4310) + // Standard Error: 2_004 + .saturating_add(Weight::from_parts(24_987, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `115 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 27_221_000 picoseconds. + Weight::from_parts(28_116_982, 4310) + // Standard Error: 1_278 + .saturating_add(Weight::from_parts(36_320, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127` + // Estimated: `4310` + // Minimum execution time: 30_492_000 picoseconds. + Weight::from_parts(31_292_727, 4310) + // Standard Error: 1_255 + .saturating_add(Weight::from_parts(15_221, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `140 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 28_633_000 picoseconds. + Weight::from_parts(29_583_862, 4310) + // Standard Error: 1_119 + .saturating_add(Weight::from_parts(27_483, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_sudo.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_sudo.rs new file mode 100644 index 0000000..86577d3 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_sudo.rs @@ -0,0 +1,99 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_sudo +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_sudo +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_sudo.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_sudo using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_sudo::WeightInfo for SubstrateWeight { + /// Storage: `Sudo::Key` (r:1 w:1) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn set_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `49` + // Estimated: `1505` + // Minimum execution time: 10_516_000 picoseconds. + Weight::from_parts(10_804_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn sudo() -> Weight { + // Proof Size summary in bytes: + // Measured: `49` + // Estimated: `1505` + // Minimum execution time: 11_972_000 picoseconds. + Weight::from_parts(12_397_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn sudo_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `49` + // Estimated: `1505` + // Minimum execution time: 11_964_000 picoseconds. + Weight::from_parts(12_171_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:1) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn remove_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `49` + // Estimated: `1505` + // Minimum execution time: 9_267_000 picoseconds. + Weight::from_parts(9_627_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_timestamp.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_timestamp.rs new file mode 100644 index 0000000..02a2eb0 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_timestamp.rs @@ -0,0 +1,77 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_timestamp +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_timestamp +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_timestamp.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_timestamp using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_timestamp::WeightInfo for SubstrateWeight { + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `1493` + // Minimum execution time: 6_296_000 picoseconds. + Weight::from_parts(6_467_000, 1493) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_finalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `57` + // Estimated: `0` + // Minimum execution time: 3_390_000 picoseconds. + Weight::from_parts(3_528_000, 0) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_tx_pause.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_tx_pause.rs new file mode 100644 index 0000000..4ee196c --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_tx_pause.rs @@ -0,0 +1,79 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_tx_pause +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_tx_pause +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_tx_pause.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_tx_pause using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_tx_pause::WeightInfo for SubstrateWeight { + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn pause() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3997` + // Minimum execution time: 14_796_000 picoseconds. + Weight::from_parts(15_140_000, 3997) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn unpause() -> Weight { + // Proof Size summary in bytes: + // Measured: `566` + // Estimated: `3997` + // Minimum execution time: 20_367_000 picoseconds. + Weight::from_parts(20_877_000, 3997) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_utility.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_utility.rs new file mode 100644 index 0000000..5386aab --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_utility.rs @@ -0,0 +1,121 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_utility +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_utility +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_utility.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_utility using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_utility::WeightInfo for SubstrateWeight { + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn batch(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 6_510_000 picoseconds. + Weight::from_parts(10_441_949, 3997) + // Standard Error: 3_829 + .saturating_add(Weight::from_parts(7_226_422, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn as_derivative() -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 12_087_000 picoseconds. + Weight::from_parts(12_487_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn batch_all(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 6_270_000 picoseconds. + Weight::from_parts(8_428_529, 3997) + // Standard Error: 4_288 + .saturating_add(Weight::from_parts(7_638_677, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + fn dispatch_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_800_000 picoseconds. + Weight::from_parts(10_005_000, 0) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn force_batch(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 6_363_000 picoseconds. + Weight::from_parts(11_799_181, 3997) + // Standard Error: 3_986 + .saturating_add(Weight::from_parts(7_238_087, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_xcm.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_xcm.rs new file mode 100644 index 0000000..73d4e48 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_xcm.rs @@ -0,0 +1,361 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_xcm.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_xcm::WeightInfo for SubstrateWeight { + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 27_967_000 picoseconds. + Weight::from_parts(28_564_000, 3540) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn teleport_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) + /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) + /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `359` + // Estimated: `607086` + // Minimum execution time: 159_104_000 picoseconds. + Weight::from_parts(163_461_000, 607086) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:1 w:0) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(160), added: 2635, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `632` + // Estimated: `607086` + // Minimum execution time: 206_597_000 picoseconds. + Weight::from_parts(210_481_000, 607086) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn execute() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 12_448_000 picoseconds. + Weight::from_parts(12_850_000, 0) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_xcm_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 10_308_000 picoseconds. + Weight::from_parts(10_472_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn force_default_xcm_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_472_000 picoseconds. + Weight::from_parts(3_595_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_subscribe_version_notify() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 33_622_000 picoseconds. + Weight::from_parts(34_072_000, 3540) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_unsubscribe_version_notify() -> Weight { + // Proof Size summary in bytes: + // Measured: `257` + // Estimated: `3722` + // Minimum execution time: 34_598_000 picoseconds. + Weight::from_parts(35_506_000, 3722) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn force_suspension() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_475_000 picoseconds. + Weight::from_parts(3_683_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_supported_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `131` + // Estimated: `11021` + // Minimum execution time: 20_771_000 picoseconds. + Weight::from_parts(21_274_000, 11021) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_version_notifiers() -> Weight { + // Proof Size summary in bytes: + // Measured: `135` + // Estimated: `11025` + // Minimum execution time: 21_067_000 picoseconds. + Weight::from_parts(21_449_000, 11025) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn already_notified_target() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `13507` + // Minimum execution time: 22_223_000 picoseconds. + Weight::from_parts(22_609_000, 13507) + .saturating_add(T::DbWeight::get().reads(5_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_current_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `6082` + // Minimum execution time: 31_347_000 picoseconds. + Weight::from_parts(32_060_000, 6082) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn notify_target_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `172` + // Estimated: `8587` + // Minimum execution time: 12_168_000 picoseconds. + Weight::from_parts(12_550_000, 8587) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_version_notify_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `11032` + // Minimum execution time: 21_253_000 picoseconds. + Weight::from_parts(21_870_000, 11032) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn migrate_and_notify_old_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `148` + // Estimated: `11038` + // Minimum execution time: 40_182_000 picoseconds. + Weight::from_parts(40_827_000, 11038) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn new_query() -> Weight { + // Proof Size summary in bytes: + // Measured: `69` + // Estimated: `1554` + // Minimum execution time: 5_644_000 picoseconds. + Weight::from_parts(5_826_000, 1554) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::Queries` (r:1 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn take_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `7706` + // Estimated: `11171` + // Minimum execution time: 33_330_000 picoseconds. + Weight::from_parts(33_695_000, 11171) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_xcm_benchmarks::generic.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_xcm_benchmarks::generic.rs new file mode 100644 index 0000000..753cd30 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_xcm_benchmarks::generic.rs @@ -0,0 +1,348 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm_benchmarks::generic +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm_benchmarks::generic +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_xcm_benchmarks::generic.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm_benchmarks::generic using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_xcm_benchmarks::generic::WeightInfo for SubstrateWeight { + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn report_holding() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 67_561_000 picoseconds. + Weight::from_parts(69_170_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + fn buy_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_557_000 picoseconds. + Weight::from_parts(2_670_000, 0) + } + /// Storage: `PolkadotXcm::Queries` (r:1 w:0) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn query_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `69` + // Estimated: `3534` + // Minimum execution time: 10_508_000 picoseconds. + Weight::from_parts(10_749_000, 3534) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn transact() -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 18_545_000 picoseconds. + Weight::from_parts(18_900_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + fn refund_surplus() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_628_000 picoseconds. + Weight::from_parts(2_801_000, 0) + } + fn set_error_handler() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_334_000 picoseconds. + Weight::from_parts(2_489_000, 0) + } + fn set_appendix() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_378_000 picoseconds. + Weight::from_parts(2_490_000, 0) + } + fn clear_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_289_000 picoseconds. + Weight::from_parts(2_469_000, 0) + } + fn descend_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_994_000 picoseconds. + Weight::from_parts(3_151_000, 0) + } + fn clear_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_323_000 picoseconds. + Weight::from_parts(2_431_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn report_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 59_355_000 picoseconds. + Weight::from_parts(60_070_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `126` + // Estimated: `3591` + // Minimum execution time: 16_086_000 picoseconds. + Weight::from_parts(16_467_000, 3591) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn trap() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_363_000 picoseconds. + Weight::from_parts(2_453_000, 0) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn subscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 28_551_000 picoseconds. + Weight::from_parts(29_222_000, 3540) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn unsubscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_211_000 picoseconds. + Weight::from_parts(5_377_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn burn_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_789_000 picoseconds. + Weight::from_parts(3_903_000, 0) + } + fn expect_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_536_000 picoseconds. + Weight::from_parts(2_681_000, 0) + } + fn expect_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_449_000 picoseconds. + Weight::from_parts(2_556_000, 0) + } + fn expect_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_379_000 picoseconds. + Weight::from_parts(2_487_000, 0) + } + fn expect_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_672_000 picoseconds. + Weight::from_parts(2_763_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn query_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 66_365_000 picoseconds. + Weight::from_parts(67_599_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + fn expect_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_932_000 picoseconds. + Weight::from_parts(8_173_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn report_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 58_856_000 picoseconds. + Weight::from_parts(60_383_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + fn clear_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_453_000 picoseconds. + Weight::from_parts(2_566_000, 0) + } + fn set_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_332_000 picoseconds. + Weight::from_parts(2_460_000, 0) + } + fn clear_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_375_000 picoseconds. + Weight::from_parts(2_454_000, 0) + } + fn set_fees_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_364_000 picoseconds. + Weight::from_parts(2_452_000, 0) + } + fn unpaid_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_456_000 picoseconds. + Weight::from_parts(2_556_000, 0) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/pallet_xcm_executor_utils.rs b/container-chains/runtime-templates/frontier/src/weights/pallet_xcm_executor_utils.rs new file mode 100644 index 0000000..e5bbc82 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/pallet_xcm_executor_utils.rs @@ -0,0 +1,99 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm_executor_utils +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm_executor_utils +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_xcm_executor_utils.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm_executor_utils using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_xcm_executor_utils::WeightInfo for SubstrateWeight { + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:0 w:1) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn set_reserve_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_476_000 picoseconds. + Weight::from_parts(9_661_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:1 w:1) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn remove_reserve_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `87` + // Estimated: `607086` + // Minimum execution time: 12_581_000 picoseconds. + Weight::from_parts(12_882_000, 607086) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:0 w:1) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn set_teleport_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_411_000 picoseconds. + Weight::from_parts(9_733_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:1 w:1) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn remove_teleport_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `87` + // Estimated: `607086` + // Minimum execution time: 12_716_000 picoseconds. + Weight::from_parts(12_960_000, 607086) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/weights/xcm/mod.rs b/container-chains/runtime-templates/frontier/src/weights/xcm/mod.rs new file mode 100644 index 0000000..a42dc2c --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/xcm/mod.rs @@ -0,0 +1,251 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +pub mod pallet_xcm_benchmarks_generic; + +use { + crate::Runtime, + frame_support::weights::Weight, + pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric, + sp_std::prelude::*, + staging_xcm::{ + latest::{prelude::*, Weight as XCMWeight}, + DoubleEncoded, + }, +}; + +trait WeighMultiAssets { + fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; +} + +impl WeighMultiAssets for MultiAssets { + fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { + weight.saturating_mul(self.inner().iter().count() as u64) + } +} + +// Values copied from statemint benchmarks +const ASSET_BURN_MAX_PROOF_SIZE: u64 = 7242; +const ASSET_MINT_MAX_PROOF_SIZE: u64 = 7242; +const ASSET_TRANSFER_MAX_PROOF_SIZE: u64 = 13412; + +// For now we are returning benchmarked weights only for generic XCM instructions. +// Fungible XCM instructions will return a fixed weight value of +// 200_000_000 ref_time and its proper PoV weight taken from statemint benchmarks. +// +// TODO: add the fungible benchmarked values once these are calculated. +pub struct XcmWeight(core::marker::PhantomData); +impl XcmWeightInfo for XcmWeight +where + Runtime: frame_system::Config, +{ + fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts( + 200_000_000u64, + ASSET_BURN_MAX_PROOF_SIZE, + )) + } + fn reserve_asset_deposited(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts(200_000_000u64, 0)) + } + fn receive_teleported_asset(_assets: &MultiAssets) -> XCMWeight { + XCMWeight::MAX + } + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &Weight, + _querier: &Option, + ) -> XCMWeight { + XcmGeneric::::query_response() + } + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts( + 200_000_000u64, + ASSET_TRANSFER_MAX_PROOF_SIZE, + )) + } + fn transfer_reserve_asset( + assets: &MultiAssets, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts( + 200_000_000u64, + ASSET_TRANSFER_MAX_PROOF_SIZE, + )) + } + fn transact( + _origin_type: &OriginKind, + _require_weight_at_most: &Weight, + _call: &DoubleEncoded, + ) -> XCMWeight { + XcmGeneric::::transact() + } + fn hrmp_new_channel_open_request( + _sender: &u32, + _max_message_size: &u32, + _max_capacity: &u32, + ) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX + } + fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX + } + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX + } + fn clear_origin() -> XCMWeight { + XcmGeneric::::clear_origin() + } + fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { + XcmGeneric::::descend_origin() + } + fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::report_error() + } + fn deposit_asset(_assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { + Weight::from_parts(200_000_000u64, ASSET_MINT_MAX_PROOF_SIZE) + } + fn deposit_reserve_asset( + _assets: &MultiAssetFilter, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + Weight::from_parts(200_000_000u64, ASSET_MINT_MAX_PROOF_SIZE) + } + fn exchange_asset( + _give: &MultiAssetFilter, + _receive: &MultiAssets, + _maximal: &bool, + ) -> XCMWeight { + Weight::MAX + } + fn initiate_reserve_withdraw( + _assets: &MultiAssetFilter, + _reserve: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + XCMWeight::from_parts(200_000_000u64, ASSET_TRANSFER_MAX_PROOF_SIZE) + } + fn initiate_teleport( + _assets: &MultiAssetFilter, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + XCMWeight::MAX + } + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight { + XcmGeneric::::report_holding() + } + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { + XcmGeneric::::buy_execution() + } + fn refund_surplus() -> XCMWeight { + XcmGeneric::::refund_surplus() + } + fn set_error_handler(_xcm: &Xcm) -> XCMWeight { + XcmGeneric::::set_error_handler() + } + fn set_appendix(_xcm: &Xcm) -> XCMWeight { + XcmGeneric::::set_appendix() + } + fn clear_error() -> XCMWeight { + XcmGeneric::::clear_error() + } + fn claim_asset(assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::claim_asset()) + } + fn trap(_code: &u64) -> XCMWeight { + XcmGeneric::::trap() + } + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> XCMWeight { + XcmGeneric::::subscribe_version() + } + fn unsubscribe_version() -> XCMWeight { + XcmGeneric::::unsubscribe_version() + } + fn burn_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> Weight { + XcmGeneric::::expect_origin() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight { + XcmGeneric::::expect_error() + } + fn expect_transact_status(_transact_status: &MaybeErrorCode) -> Weight { + XcmGeneric::::expect_transact_status() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::query_pallet() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> Weight { + XcmGeneric::::expect_pallet() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_transact_status() + } + fn clear_transact_status() -> Weight { + XcmGeneric::::clear_transact_status() + } + fn universal_origin(_: &Junction) -> Weight { + Weight::MAX + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + Weight::MAX + } + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn set_fees_mode(_: &bool) -> Weight { + XcmGeneric::::set_fees_mode() + } + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() + } + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() + } + fn alias_origin(_: &MultiLocation) -> Weight { + // XCM Executor does not currently support alias origin operations + Weight::MAX + } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { + XcmGeneric::::unpaid_execution() + } +} diff --git a/container-chains/runtime-templates/frontier/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/container-chains/runtime-templates/frontier/src/weights/xcm/pallet_xcm_benchmarks_generic.rs new file mode 100644 index 0000000..b1f1752 --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -0,0 +1,348 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm_benchmarks::generic +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-frontier-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm_benchmarks::generic +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-runtime-template-xcm.hbs +// --json-file +// raw.json +// --output +// tmp/frontier_template_weights/pallet_xcm_benchmarks::generic.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm_benchmarks::generic using the Substrate node and recommended hardware. +pub struct WeightInfo(PhantomData); +impl WeightInfo { + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn report_holding() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 70_314_000 picoseconds. + Weight::from_parts(71_644_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + pub(crate) fn buy_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_796_000 picoseconds. + Weight::from_parts(2_914_000, 0) + } + /// Storage: `PolkadotXcm::Queries` (r:1 w:0) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn query_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `69` + // Estimated: `3534` + // Minimum execution time: 10_662_000 picoseconds. + Weight::from_parts(11_031_000, 3534) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + pub(crate) fn transact() -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 18_559_000 picoseconds. + Weight::from_parts(19_014_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + pub(crate) fn refund_surplus() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_890_000 picoseconds. + Weight::from_parts(3_042_000, 0) + } + pub(crate) fn set_error_handler() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_675_000 picoseconds. + Weight::from_parts(2_798_000, 0) + } + pub(crate) fn set_appendix() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_648_000 picoseconds. + Weight::from_parts(2_741_000, 0) + } + pub(crate) fn clear_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_624_000 picoseconds. + Weight::from_parts(2_731_000, 0) + } + pub(crate) fn descend_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_276_000 picoseconds. + Weight::from_parts(3_439_000, 0) + } + pub(crate) fn clear_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_602_000 picoseconds. + Weight::from_parts(2_695_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn report_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 60_682_000 picoseconds. + Weight::from_parts(61_905_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn claim_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `126` + // Estimated: `3591` + // Minimum execution time: 16_316_000 picoseconds. + Weight::from_parts(16_811_000, 3591) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + pub(crate) fn trap() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_560_000 picoseconds. + Weight::from_parts(2_687_000, 0) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn subscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 28_744_000 picoseconds. + Weight::from_parts(29_222_000, 3540) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn unsubscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_489_000 picoseconds. + Weight::from_parts(5_637_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + pub(crate) fn burn_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_125_000 picoseconds. + Weight::from_parts(4_247_000, 0) + } + pub(crate) fn expect_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_703_000 picoseconds. + Weight::from_parts(2_900_000, 0) + } + pub(crate) fn expect_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_753_000 picoseconds. + Weight::from_parts(2_840_000, 0) + } + pub(crate) fn expect_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_614_000 picoseconds. + Weight::from_parts(2_730_000, 0) + } + pub(crate) fn expect_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_893_000 picoseconds. + Weight::from_parts(3_000_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn query_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 68_185_000 picoseconds. + Weight::from_parts(69_751_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + pub(crate) fn expect_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_779_000 picoseconds. + Weight::from_parts(8_047_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn report_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 61_210_000 picoseconds. + Weight::from_parts(61_906_000, 3631) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_629_000 picoseconds. + Weight::from_parts(2_739_000, 0) + } + pub(crate) fn set_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_598_000 picoseconds. + Weight::from_parts(2_697_000, 0) + } + pub(crate) fn clear_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_639_000 picoseconds. + Weight::from_parts(2_732_000, 0) + } + pub(crate) fn set_fees_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_637_000 picoseconds. + Weight::from_parts(2_710_000, 0) + } + pub(crate) fn unpaid_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_769_000 picoseconds. + Weight::from_parts(2_857_000, 0) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/frontier/src/xcm_config.rs b/container-chains/runtime-templates/frontier/src/xcm_config.rs new file mode 100644 index 0000000..41639fb --- /dev/null +++ b/container-chains/runtime-templates/frontier/src/xcm_config.rs @@ -0,0 +1,500 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + super::{ + currency::MICROUNIT, precompiles::FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, weights, + weights::xcm::XcmWeight as XcmGenericWeights, AccountId, AllPalletsWithSystem, AssetRate, + Balance, Balances, ForeignAssetsCreator, MaintenanceMode, MessageQueue, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent, + RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, + }, + ccp_xcm::SignedToAccountKey20, + cumulus_primitives_core::{AggregateMessageOrigin, ParaId}, + frame_support::{ + parameter_types, + traits::{Everything, Nothing, PalletInfoAccess, TransformOrigin}, + weights::Weight, + }, + frame_system::EnsureRoot, + pallet_evm_precompileset_assets_erc20::AccountIdAssetIdConversion, + pallet_foreign_asset_creator::{ + AssetBalance, AssetId as AssetIdOf, ForeignAssetCreatedHook, ForeignAssetDestroyedHook, + }, + pallet_xcm::XcmPassthrough, + pallet_xcm_executor_utils::{ + filters::{IsReserveFilter, IsTeleportFilter}, + DefaultTrustPolicy, + }, + parachains_common::{ + message_queue::{NarrowOriginToSibling, ParaIdToSibling}, + xcm_config::AssetFeeAsExistentialDepositMultiplier, + }, + polkadot_runtime_common::xcm_sender::ExponentialPrice, + sp_core::{ConstU32, H160}, + sp_runtime::Perbill, + sp_std::vec::Vec, + staging_xcm::latest::prelude::*, + staging_xcm_builder::{ + AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FungibleAdapter, + IsConcrete, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, + }, + staging_xcm_executor::XcmExecutor, +}; +parameter_types! { + // Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a MultiLocation: (Self Balances pallet index) + // We use the RELATIVE multilocation + pub SelfReserve: MultiLocation = MultiLocation { + parents:0, + interior: Junctions::X1( + PalletInstance(::index() as u8) + ) + }; + + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); + + // TODO: revisit + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + + // The relay chain Origin type + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + + pub const MaxAssetsIntoHolding: u32 = 64; + + /// Maximum number of instructions in a single XCM fragment. A sanity check against + /// weight caculations getting too crazy. + pub MaxInstructions: u32 = 100; + + // The universal location within the global consensus system + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); + + pub const BaseDeliveryFee: u128 = 100 * MICROUNIT; +} + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +// For benchmarking, we cannot use the describeFamily +// the benchmark is written to be able to convert an AccountId32, but describeFamily prevents this +#[cfg(not(feature = "runtime-benchmarks"))] +type Descriptor = staging_xcm_builder::DescribeFamily; +#[cfg(feature = "runtime-benchmarks")] +type Descriptor = staging_xcm_builder::DescribeAllTerminal; + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // If we receive a MultiLocation of type AccountKey20, just generate a native account + AccountKey20Aliases, + // Generate remote accounts according to polkadot standards + staging_xcm_builder::HashedDescription, +); + +/// Local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountKey20; + +/// Means for transacting the native currency on this chain. +pub type CurrencyTransactor = FungibleAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Convert an XCM MultiLocation into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports of `Balances`. + (), +>; + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `RuntimeOrigin::Signed` origin of the same 32-byte value. + SignedAccountKey20AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +/// Means for transacting assets on this chain. +pub type AssetTransactors = (CurrencyTransactor, ForeignFungiblesTransactor); +pub type XcmWeigher = + WeightInfoBounds, RuntimeCall, MaxInstructions>; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +pub struct XcmConfig; +impl staging_xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = IsReserveFilter; + type IsTeleporter = IsTeleportFilter; + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = XcmWeigher; + type Trader = ( + UsingComponents, + cumulus_primitives_utility::TakeFirstAssetTrader< + AccountId, + AssetRateAsMultiplier, + // Use this currency when it is a fungible asset matching the given location or name: + (ConvertedConcreteId,), + ForeignAssets, + (), + >, + ); + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // TODO pallet-xcm weights + type WeightInfo = weights::pallet_xcm::SubstrateWeight; + type AdminOrigin = EnsureRoot; +} + +pub type PriceForSiblingParachainDelivery = + ExponentialPrice; + +pub type PriceForParentDelivery = + ExponentialPrice; + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = weights::cumulus_pallet_xcmp_queue::SubstrateWeight; + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; + type XcmpQueue = TransformOrigin; + type MaxInboundSuspended = sp_core::ConstU32<1_000>; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +parameter_types! { + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type WeightInfo = weights::cumulus_pallet_dmp_queue::SubstrateWeight; + type RuntimeEvent = RuntimeEvent; + type DmpSink = frame_support::traits::EnqueueWithOrigin; +} + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_message_queue::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = staging_xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + XcmExecutor, + RuntimeCall, + >; + type Size = u32; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + // NarrowOriginToSibling calls XcmpQueue's is_pause if Origin is sibling. Allows all other origins + type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + // TODO verify values + type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; + type MaxStale = sp_core::ConstU32<8>; + type ServiceWeight = MessageQueueServiceWeight; +} + +parameter_types! { + // we just reuse the same deposits + pub const ForeignAssetsAssetDeposit: Balance = 0; + pub const ForeignAssetsAssetAccountDeposit: Balance = 0; + pub const ForeignAssetsApprovalDeposit: Balance = 0; + pub const ForeignAssetsAssetsStringLimit: u32 = 50; + pub const ForeignAssetsMetadataDepositBase: Balance = 0; + pub const ForeignAssetsMetadataDepositPerByte: Balance = 0; + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +#[cfg(feature = "runtime-benchmarks")] +/// Simple conversion of `u32` into an `AssetId` for use in benchmarking. +pub struct ForeignAssetBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_assets::BenchmarkHelper for ForeignAssetBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> AssetId { + id.try_into() + .expect("number too large to create benchmarks") + } +} +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_rate::AssetKindFactory for ForeignAssetBenchmarkHelper { + fn create_asset_kind(id: u32) -> AssetId { + id.try_into() + .expect("number too large to create benchmarks") + } +} + +// Instruct how to go from an H160 to an AssetID +// We just take the lowest 2 bytes +impl AccountIdAssetIdConversion for Runtime { + /// The way to convert an account to assetId is by ensuring that the prefix is [0xFF, 18] + /// and by taking the lowest 2 bytes as the assetId + fn account_to_asset_id(account: AccountId) -> Option<(Vec, AssetId)> { + let h160_account: H160 = account.into(); + let mut data = [0u8; 2]; + let (prefix_part, id_part) = h160_account.as_fixed_bytes().split_at(18); + if prefix_part == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX { + data.copy_from_slice(id_part); + let asset_id: AssetId = u16::from_be_bytes(data); + Some((prefix_part.to_vec(), asset_id)) + } else { + None + } + } + + // The opposite conversion + fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> AccountId { + let mut data = [0u8; 20]; + data[0..18].copy_from_slice(prefix); + data[18..20].copy_from_slice(&asset_id.to_be_bytes()); + AccountId::from(data) + } +} + +pub type AssetId = u16; +pub type ForeignAssetsInstance = pallet_assets::Instance1; +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetIdParameter = AssetId; + type Currency = Balances; + type CreateOrigin = frame_support::traits::NeverEnsureOrigin; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ForeignAssetsAssetDeposit; + type MetadataDepositBase = ForeignAssetsMetadataDepositBase; + type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; + type ApprovalDeposit = ForeignAssetsApprovalDeposit; + type StringLimit = ForeignAssetsAssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = weights::pallet_assets::SubstrateWeight; + type CallbackHandle = (); + type AssetAccountDeposit = ForeignAssetsAssetAccountDeposit; + type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ForeignAssetBenchmarkHelper; +} + +pub struct RevertCodePrecompileHook; + +impl ForeignAssetCreatedHook, AssetBalance> + for RevertCodePrecompileHook +{ + fn on_asset_created( + _foreign_asset: &MultiLocation, + asset_id: &AssetIdOf, + _min_balance: &AssetBalance, + ) { + let revert_bytecode = [0x60, 0x00, 0x60, 0x00, 0xFD].to_vec(); + let prefix_slice = [255u8; 18]; + let account_id = Runtime::asset_id_to_account(prefix_slice.as_slice(), *asset_id); + + pallet_evm::Pallet::::create_account(account_id.into(), revert_bytecode.clone()); + } +} + +impl ForeignAssetDestroyedHook> for RevertCodePrecompileHook { + fn on_asset_destroyed(_foreign_asset: &MultiLocation, asset_id: &AssetIdOf) { + let prefix_slice = [255u8; 18]; + let account_id = Runtime::asset_id_to_account(prefix_slice.as_slice(), *asset_id); + + pallet_evm::Pallet::::remove_account(&account_id.into()); + } +} + +impl pallet_foreign_asset_creator::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForeignAsset = MultiLocation; + type ForeignAssetCreatorOrigin = EnsureRoot; + type ForeignAssetModifierOrigin = EnsureRoot; + type ForeignAssetDestroyerOrigin = EnsureRoot; + type Fungibles = ForeignAssets; + type WeightInfo = weights::pallet_foreign_asset_creator::SubstrateWeight; + type OnForeignAssetCreated = RevertCodePrecompileHook; + type OnForeignAssetDestroyed = RevertCodePrecompileHook; +} + +impl pallet_asset_rate::Config for Runtime { + type CreateOrigin = EnsureRoot; + type RemoveOrigin = EnsureRoot; + type UpdateOrigin = EnsureRoot; + type Currency = Balances; + type AssetKind = AssetId; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_asset_rate::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ForeignAssetBenchmarkHelper; +} + +parameter_types! { + pub const TrustPolicyMaxAssets: u32 = 1000; + pub const AllNativeTrustPolicy: DefaultTrustPolicy = DefaultTrustPolicy::AllNative; + pub const AllNeverTrustPolicy: DefaultTrustPolicy = DefaultTrustPolicy::Never; +} +impl pallet_xcm_executor_utils::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type TrustPolicyMaxAssets = TrustPolicyMaxAssets; + type ReserveDefaultTrustPolicy = AllNativeTrustPolicy; + type SetReserveTrustOrigin = EnsureRoot; + type TeleportDefaultTrustPolicy = AllNeverTrustPolicy; + type SetTeleportTrustOrigin = EnsureRoot; + type WeightInfo = weights::pallet_xcm_executor_utils::SubstrateWeight; +} + +use { + crate::ForeignAssets, + staging_xcm_builder::{FungiblesAdapter, NoChecking}, + staging_xcm_executor::traits::JustTry, +}; + +/// Means for transacting foreign assets from different global consensus. +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + ForeignAssets, + // Use this currency when it is a fungible asset matching the given location or name: + (ConvertedConcreteId,), + // Convert an XCM MultiLocation into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont need to check teleports here. + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; + +/// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. +pub type AssetRateAsMultiplier = + AssetFeeAsExistentialDepositMultiplier; + +#[test] +fn test_asset_id_to_account_conversion() { + let prefix_slice = [255u8].repeat(18); + let asset_ids_to_check = vec![0u16, 123u16, 3453u16, 10000u16, 65535u16]; + for current_asset_id in asset_ids_to_check { + let account_id = Runtime::asset_id_to_account(prefix_slice.as_slice(), current_asset_id); + assert_eq!( + account_id.to_string().to_lowercase(), + String::from("0xffffffffffffffffffffffffffffffffffff") + + format!("{:04x}", current_asset_id).as_str() + ); + } +} diff --git a/container-chains/runtime-templates/simple/Cargo.toml b/container-chains/runtime-templates/simple/Cargo.toml new file mode 100644 index 0000000..8cd1d2f --- /dev/null +++ b/container-chains/runtime-templates/simple/Cargo.toml @@ -0,0 +1,311 @@ +[package] +name = "container-chain-template-simple-runtime" +authors = { workspace = true } +description = "Simple container chain template runtime" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +hex-literal = { workspace = true, optional = true } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive" ] } +scale-info = { workspace = true, features = [ "derive" ] } +serde = { workspace = true, optional = true, features = [ "derive" ] } +smallvec = { workspace = true } + +# Local +dp-consensus = { workspace = true } +dp-impl-tanssi-pallets-config = { workspace = true } +dp-slot-duration-runtime-api = { workspace = true } +pallet-cc-authorities-noting = { workspace = true } +runtime-common = { workspace = true } + +# Moonkit +async-backing-primitives = { workspace = true } +nimbus-primitives = { workspace = true } +pallet-async-backing = { workspace = true } +pallet-author-inherent = { workspace = true } +pallet-foreign-asset-creator = { workspace = true } +pallet-maintenance-mode = { workspace = true, features = [ "xcm-support" ] } +pallet-migrations = { workspace = true } +xcm-primitives = { workspace = true } + +# Dancekit +pallet-xcm-executor-utils = { workspace = true } + +# Substrate +frame-executive = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-system-rpc-runtime-api = { workspace = true } +pallet-asset-rate = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true } +pallet-message-queue = { workspace = true } +pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } +pallet-root-testing = { workspace = true } +pallet-session = { workspace = true } +pallet-sudo = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-transaction-payment = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } +pallet-tx-pause = { workspace = true } +pallet-utility = { workspace = true } +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-consensus-aura = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true } +sp-debug-derive = { workspace = true } +sp-genesis-builder = { workspace = true } +sp-inherents = { workspace = true } +sp-offchain = { workspace = true } +sp-runtime = { workspace = true } +sp-session = { workspace = true } +sp-std = { workspace = true } +sp-transaction-pool = { workspace = true } +sp-trie = { workspace = true } + +sp-version = { workspace = true } + +pallet-insecure-randomness-collective-flip = { workspace = true } + + +# Local Dependencies +pallet-template = { workspace = true } +pallet-sortition-sum-game = { workspace = true } +pallet-schelling-game-shared = { workspace = true } +pallet-profile-validation = { workspace = true } +pallet-shared-storage = { workspace = true } +pallet-positive-externality = { workspace = true } +pallet-department-funding = { workspace = true } +pallet-project-tips = { workspace = true } +profile-validation-runtime-api = { workspace = true } +positive-externality-runtime-api = { workspace = true } +department-funding-runtime-api = { workspace = true } +project-tips-runtime-api = { workspace = true } + + +# Polkadot +pallet-xcm = { workspace = true } +pallet-xcm-benchmarks = { workspace = true, optional = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-runtime-common = { workspace = true } +staging-xcm = { workspace = true } +staging-xcm-builder = { workspace = true } +staging-xcm-executor = { workspace = true } + +# Cumulus +cumulus-pallet-dmp-queue = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true } +cumulus-pallet-session-benchmarking = { workspace = true } +cumulus-pallet-xcm = { workspace = true } +cumulus-pallet-xcmp-queue = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-timestamp = { workspace = true } +cumulus-primitives-utility = { workspace = true } +parachain-info = { workspace = true } +parachains-common = { workspace = true } + +# Benchmarking +frame-benchmarking = { workspace = true, optional = true } +frame-system-benchmarking = { workspace = true, optional = true } +frame-try-runtime = { workspace = true, optional = true } +[build-dependencies] +substrate-wasm-builder = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "async-backing-primitives/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-session-benchmarking/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "dp-consensus/std", + "dp-impl-tanssi-pallets-config/std", + "dp-slot-duration-runtime-api/std", + "frame-benchmarking?/std", + "frame-executive/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "frame-try-runtime/std", + "log/std", + "nimbus-primitives/std", + "pallet-asset-rate/std", + "pallet-assets/std", + "pallet-async-backing/std", + "pallet-author-inherent/std", + "pallet-balances/std", + "pallet-cc-authorities-noting/std", + "pallet-foreign-asset-creator/std", + "pallet-maintenance-mode/std", + "pallet-message-queue/std", + "pallet-migrations/std", + "pallet-multisig/std", + "pallet-proxy/std", + "pallet-root-testing/std", + "pallet-session/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-tx-pause/std", + "pallet-utility/std", + "pallet-xcm-benchmarks?/std", + "pallet-xcm-executor-utils/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "parity-scale-codec/std", + "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", + "runtime-common/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-consensus-slots/std", + "sp-core/std", + "sp-debug-derive/std", + "sp-genesis-builder/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-trie/std", + "sp-version/std", + "staging-xcm-builder/std", + "staging-xcm-executor/std", + "staging-xcm/std", + "xcm-primitives/std", + "pallet-insecure-randomness-collective-flip/std", + "pallet-template/std", + "pallet-sortition-sum-game/std", + "pallet-schelling-game-shared/std", + "pallet-profile-validation/std", + "pallet-shared-storage/std", + "pallet-positive-externality/std", + "pallet-department-funding/std", + "pallet-project-tips/std", + "profile-validation-runtime-api/std", + "positive-externality-runtime-api/std", + "department-funding-runtime-api/std", + "project-tips-runtime-api/std", +] + +# Allow to print logs details (no wasm:stripped) +force-debug = [ "sp-debug-derive/force-debug" ] + +runtime-benchmarks = [ + "cumulus-pallet-dmp-queue/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-utility/runtime-benchmarks", + "dp-consensus/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "hex-literal", + "nimbus-primitives/runtime-benchmarks", + "pallet-asset-rate/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-author-inherent/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-cc-authorities-noting/runtime-benchmarks", + "pallet-foreign-asset-creator/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-tx-pause/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-executor-utils/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "runtime-common/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "staging-xcm-builder/runtime-benchmarks", + "staging-xcm-executor/runtime-benchmarks", + "xcm-primitives/runtime-benchmarks", + "pallet-template/runtime-benchmarks", + "pallet-sortition-sum-game/runtime-benchmarks", + "pallet-schelling-game-shared/runtime-benchmarks", + "pallet-profile-validation/runtime-benchmarks", + "pallet-shared-storage/runtime-benchmarks", + "pallet-positive-externality/runtime-benchmarks", + "pallet-department-funding/runtime-benchmarks", + "pallet-project-tips/runtime-benchmarks", +] + +try-runtime = [ + "cumulus-pallet-dmp-queue/try-runtime", + "cumulus-pallet-parachain-system/try-runtime", + "cumulus-pallet-xcm/try-runtime", + "cumulus-pallet-xcmp-queue/try-runtime", + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime", + "frame-try-runtime/try-runtime", + "nimbus-primitives/try-runtime", + "pallet-asset-rate/try-runtime", + "pallet-assets/try-runtime", + "pallet-async-backing/try-runtime", + "pallet-author-inherent/try-runtime", + "pallet-balances/try-runtime", + "pallet-cc-authorities-noting/try-runtime", + "pallet-foreign-asset-creator/try-runtime", + "pallet-maintenance-mode/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", + "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", + "pallet-root-testing/try-runtime", + "pallet-session/try-runtime", + "pallet-sudo/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "pallet-tx-pause/try-runtime", + "pallet-utility/try-runtime", + "pallet-xcm-executor-utils/try-runtime", + "pallet-xcm/try-runtime", + "parachain-info/try-runtime", + "polkadot-runtime-common/try-runtime", + "runtime-common/try-runtime", + "sp-runtime/try-runtime", + "pallet-template/try-runtime", + "pallet-sortition-sum-game/try-runtime", + "pallet-schelling-game-shared/try-runtime", + "pallet-profile-validation/try-runtime", + "pallet-shared-storage/try-runtime", + "pallet-positive-externality/try-runtime", + "pallet-department-funding/try-runtime", + "pallet-project-tips/try-runtime", +] diff --git a/container-chains/runtime-templates/simple/build.rs b/container-chains/runtime-templates/simple/build.rs new file mode 100644 index 0000000..9e48e37 --- /dev/null +++ b/container-chains/runtime-templates/simple/build.rs @@ -0,0 +1,25 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_06_00-128722.txt b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_06_00-128722.txt new file mode 100644 index 0000000..2284750 --- /dev/null +++ b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_06_00-128722.txt @@ -0,0 +1,78 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + 0: 0x7f3e8fd7528f - std::backtrace_rs::backtrace::libunwind::trace::he4ee80166a02c846 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5 + 1: 0x7f3e8fd7528f - std::backtrace_rs::backtrace::trace_unsynchronized::h4665ca2a08e42cea + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 + 2: 0x7f3e8fd7528f - std::backtrace::Backtrace::create::h53f88232b3c879c4 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:331:13 + 3: 0x7f3e8fd751d0 - std::backtrace::Backtrace::force_capture::h9de6994a0c478360 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:312:9 + 4: 0x7f3e92c290b7 - std[e4dfbc2c3f4b09f1]::panicking::update_hook::>::{closure#0} + 5: 0x7f3e8fd8fac0 - as core::ops::function::Fn>::call::h022ca2c0d8c21c9e + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2034:9 + 6: 0x7f3e8fd8fac0 - std::panicking::rust_panic_with_hook::h0ad14d90dcf5224f + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:783:13 + 7: 0x7f3e92c51fa4 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic::::{closure#0} + 8: 0x7f3e92c4ed16 - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> + 9: 0x7f3e92c4e9f6 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic:: + 10: 0x7f3e92c5b451 - ::emit_producing_guarantee + 11: 0x7f3e930b3e51 - rustc_middle[fda44fdb505d3e7f]::util::bug::opt_span_bug_fmt::::{closure#0} + 12: 0x7f3e930973aa - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} + 13: 0x7f3e93097228 - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> + 14: 0x7f3e90e85df0 - rustc_middle[fda44fdb505d3e7f]::util::bug::bug_fmt + 15: 0x7f3e946390fe - >::fold_ty + 16: 0x7f3e9457a3d0 - ::ctor_sub_tys + 17: 0x7f3e94579fae - >::wild_from_ctor + 18: 0x7f3e94565b6e - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 19: 0x7f3e945632c3 - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 20: 0x7f3e9455293a - rustc_pattern_analysis[4bbf7d37c729d81f]::analyze_match + 21: 0x7f3e916a76ee - ::is_let_irrefutable + 22: 0x7f3e916a1e8d - ::visit_expr + 23: 0x7f3e916a12fd - ::visit_expr + 24: 0x7f3e916a1913 - ::visit_expr + 25: 0x7f3e916a12fd - ::visit_expr + 26: 0x7f3e916a23ee - ::visit_expr + 27: 0x7f3e916a12fd - ::visit_expr + 28: 0x7f3e945a778b - rustc_mir_build[95b41b8ff12a5765]::thir::pattern::check_match::check_match + 29: 0x7f3e945a7435 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 30: 0x7f3e94518982 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 31: 0x7f3e94518695 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace + 32: 0x7f3e9459968b - rustc_mir_build[95b41b8ff12a5765]::build::mir_built + 33: 0x7f3e94599595 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 34: 0x7f3e93f3deed - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 35: 0x7f3e93f3da4c - rustc_query_impl[e4152ad88c3d6c78]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace + 36: 0x7f3e94595315 - rustc_mir_build[95b41b8ff12a5765]::check_unsafety::check_unsafety + 37: 0x7f3e94595109 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 38: 0x7f3e94594746 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 39: 0x7f3e9459444f - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace + 40: 0x7f3e94711fbf - rustc_interface[ba2b6dc4c96cb491]::passes::analysis + 41: 0x7f3e947118e5 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 42: 0x7f3e94b7f3a2 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 43: 0x7f3e94b7f149 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 44: 0x7f3e949fc814 - rustc_interface[ba2b6dc4c96cb491]::interface::run_compiler::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0} + 45: 0x7f3e94d3eb6e - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>> + 46: 0x7f3e94d3e9ca - <::spawn_unchecked_, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#1} as core[836963c7c1decc11]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 47: 0x7f3e8fd99145 - as core::ops::function::FnOnce>::call_once::h19b9e642d37e7272 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 48: 0x7f3e8fd99145 - as core::ops::function::FnOnce>::call_once::h97265befc434d3ae + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 49: 0x7f3e8fd99145 - std::sys::pal::unix::thread::Thread::new::thread_start::h420dad5cf01a9f35 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys/pal/unix/thread.rs:108:17 + 50: 0x7f3e8fa94ac3 - start_thread + at ./nptl/pthread_create.c:442:8 + 51: 0x7f3e8fb26850 - __GI___clone3 + at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 + 52: 0x0 - + + +rustc version: 1.78.0 (9b00956e5 2024-04-29) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [check_match] match-checking `::filter` +#1 [mir_built] building MIR for `::filter` +#2 [check_unsafety] unsafety-checking `::filter` +#3 [analysis] running analysis passes on this crate +end of query stack diff --git a/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_06_14-128872.txt b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_06_14-128872.txt new file mode 100644 index 0000000..456d734 --- /dev/null +++ b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_06_14-128872.txt @@ -0,0 +1,78 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + 0: 0x778f2e1ba28f - std::backtrace_rs::backtrace::libunwind::trace::he4ee80166a02c846 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5 + 1: 0x778f2e1ba28f - std::backtrace_rs::backtrace::trace_unsynchronized::h4665ca2a08e42cea + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 + 2: 0x778f2e1ba28f - std::backtrace::Backtrace::create::h53f88232b3c879c4 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:331:13 + 3: 0x778f2e1ba1d0 - std::backtrace::Backtrace::force_capture::h9de6994a0c478360 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:312:9 + 4: 0x778f2b0290b7 - std[e4dfbc2c3f4b09f1]::panicking::update_hook::>::{closure#0} + 5: 0x778f2e1d4ac0 - as core::ops::function::Fn>::call::h022ca2c0d8c21c9e + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2034:9 + 6: 0x778f2e1d4ac0 - std::panicking::rust_panic_with_hook::h0ad14d90dcf5224f + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:783:13 + 7: 0x778f2b051fa4 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic::::{closure#0} + 8: 0x778f2b04ed16 - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> + 9: 0x778f2b04e9f6 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic:: + 10: 0x778f2b05b451 - ::emit_producing_guarantee + 11: 0x778f2b4b3e51 - rustc_middle[fda44fdb505d3e7f]::util::bug::opt_span_bug_fmt::::{closure#0} + 12: 0x778f2b4973aa - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} + 13: 0x778f2b497228 - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> + 14: 0x778f29285df0 - rustc_middle[fda44fdb505d3e7f]::util::bug::bug_fmt + 15: 0x778f2ca390fe - >::fold_ty + 16: 0x778f2c97a3d0 - ::ctor_sub_tys + 17: 0x778f2c979fae - >::wild_from_ctor + 18: 0x778f2c965b6e - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 19: 0x778f2c9632c3 - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 20: 0x778f2c95293a - rustc_pattern_analysis[4bbf7d37c729d81f]::analyze_match + 21: 0x778f29aa76ee - ::is_let_irrefutable + 22: 0x778f29aa1e8d - ::visit_expr + 23: 0x778f29aa12fd - ::visit_expr + 24: 0x778f29aa1913 - ::visit_expr + 25: 0x778f29aa12fd - ::visit_expr + 26: 0x778f29aa23ee - ::visit_expr + 27: 0x778f29aa12fd - ::visit_expr + 28: 0x778f2c9a778b - rustc_mir_build[95b41b8ff12a5765]::thir::pattern::check_match::check_match + 29: 0x778f2c9a7435 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 30: 0x778f2c918982 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 31: 0x778f2c918695 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace + 32: 0x778f2c99968b - rustc_mir_build[95b41b8ff12a5765]::build::mir_built + 33: 0x778f2c999595 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 34: 0x778f2c33deed - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 35: 0x778f2c33da4c - rustc_query_impl[e4152ad88c3d6c78]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace + 36: 0x778f2c995315 - rustc_mir_build[95b41b8ff12a5765]::check_unsafety::check_unsafety + 37: 0x778f2c995109 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 38: 0x778f2c994746 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 39: 0x778f2c99444f - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace + 40: 0x778f2cb11fbf - rustc_interface[ba2b6dc4c96cb491]::passes::analysis + 41: 0x778f2cb118e5 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 42: 0x778f2cf7f3a2 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 43: 0x778f2cf7f149 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 44: 0x778f2cdfc814 - rustc_interface[ba2b6dc4c96cb491]::interface::run_compiler::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0} + 45: 0x778f2d13eb6e - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>> + 46: 0x778f2d13e9ca - <::spawn_unchecked_, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#1} as core[836963c7c1decc11]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 47: 0x778f2e1de145 - as core::ops::function::FnOnce>::call_once::h19b9e642d37e7272 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 48: 0x778f2e1de145 - as core::ops::function::FnOnce>::call_once::h97265befc434d3ae + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 49: 0x778f2e1de145 - std::sys::pal::unix::thread::Thread::new::thread_start::h420dad5cf01a9f35 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys/pal/unix/thread.rs:108:17 + 50: 0x778f27e94ac3 - start_thread + at ./nptl/pthread_create.c:442:8 + 51: 0x778f27f26850 - __GI___clone3 + at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 + 52: 0x0 - + + +rustc version: 1.78.0 (9b00956e5 2024-04-29) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [check_match] match-checking `::filter` +#1 [mir_built] building MIR for `::filter` +#2 [check_unsafety] unsafety-checking `::filter` +#3 [analysis] running analysis passes on this crate +end of query stack diff --git a/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_07_54-129055.txt b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_07_54-129055.txt new file mode 100644 index 0000000..e585e1b --- /dev/null +++ b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_07_54-129055.txt @@ -0,0 +1,78 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + 0: 0x7bd12022428f - std::backtrace_rs::backtrace::libunwind::trace::he4ee80166a02c846 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5 + 1: 0x7bd12022428f - std::backtrace_rs::backtrace::trace_unsynchronized::h4665ca2a08e42cea + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 + 2: 0x7bd12022428f - std::backtrace::Backtrace::create::h53f88232b3c879c4 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:331:13 + 3: 0x7bd1202241d0 - std::backtrace::Backtrace::force_capture::h9de6994a0c478360 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:312:9 + 4: 0x7bd11d0290b7 - std[e4dfbc2c3f4b09f1]::panicking::update_hook::>::{closure#0} + 5: 0x7bd12023eac0 - as core::ops::function::Fn>::call::h022ca2c0d8c21c9e + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2034:9 + 6: 0x7bd12023eac0 - std::panicking::rust_panic_with_hook::h0ad14d90dcf5224f + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:783:13 + 7: 0x7bd11d051fa4 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic::::{closure#0} + 8: 0x7bd11d04ed16 - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> + 9: 0x7bd11d04e9f6 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic:: + 10: 0x7bd11d05b451 - ::emit_producing_guarantee + 11: 0x7bd11d4b3e51 - rustc_middle[fda44fdb505d3e7f]::util::bug::opt_span_bug_fmt::::{closure#0} + 12: 0x7bd11d4973aa - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} + 13: 0x7bd11d497228 - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> + 14: 0x7bd11b285df0 - rustc_middle[fda44fdb505d3e7f]::util::bug::bug_fmt + 15: 0x7bd11ea390fe - >::fold_ty + 16: 0x7bd11e97a3d0 - ::ctor_sub_tys + 17: 0x7bd11e979fae - >::wild_from_ctor + 18: 0x7bd11e965b6e - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 19: 0x7bd11e9632c3 - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 20: 0x7bd11e95293a - rustc_pattern_analysis[4bbf7d37c729d81f]::analyze_match + 21: 0x7bd11baa76ee - ::is_let_irrefutable + 22: 0x7bd11baa1e8d - ::visit_expr + 23: 0x7bd11baa12fd - ::visit_expr + 24: 0x7bd11baa1913 - ::visit_expr + 25: 0x7bd11baa12fd - ::visit_expr + 26: 0x7bd11baa23ee - ::visit_expr + 27: 0x7bd11baa12fd - ::visit_expr + 28: 0x7bd11e9a778b - rustc_mir_build[95b41b8ff12a5765]::thir::pattern::check_match::check_match + 29: 0x7bd11e9a7435 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 30: 0x7bd11e918982 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 31: 0x7bd11e918695 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace + 32: 0x7bd11e99968b - rustc_mir_build[95b41b8ff12a5765]::build::mir_built + 33: 0x7bd11e999595 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 34: 0x7bd11e33deed - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 35: 0x7bd11e33da4c - rustc_query_impl[e4152ad88c3d6c78]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace + 36: 0x7bd11e995315 - rustc_mir_build[95b41b8ff12a5765]::check_unsafety::check_unsafety + 37: 0x7bd11e995109 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 38: 0x7bd11e994746 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 39: 0x7bd11e99444f - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace + 40: 0x7bd11eb11fbf - rustc_interface[ba2b6dc4c96cb491]::passes::analysis + 41: 0x7bd11eb118e5 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 42: 0x7bd11ef7f3a2 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 43: 0x7bd11ef7f149 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 44: 0x7bd11edfc814 - rustc_interface[ba2b6dc4c96cb491]::interface::run_compiler::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0} + 45: 0x7bd11f13eb6e - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>> + 46: 0x7bd11f13e9ca - <::spawn_unchecked_, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#1} as core[836963c7c1decc11]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 47: 0x7bd120248145 - as core::ops::function::FnOnce>::call_once::h19b9e642d37e7272 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 48: 0x7bd120248145 - as core::ops::function::FnOnce>::call_once::h97265befc434d3ae + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 49: 0x7bd120248145 - std::sys::pal::unix::thread::Thread::new::thread_start::h420dad5cf01a9f35 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys/pal/unix/thread.rs:108:17 + 50: 0x7bd119e94ac3 - start_thread + at ./nptl/pthread_create.c:442:8 + 51: 0x7bd119f26850 - __GI___clone3 + at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 + 52: 0x0 - + + +rustc version: 1.78.0 (9b00956e5 2024-04-29) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [check_match] match-checking `::filter` +#1 [mir_built] building MIR for `::filter` +#2 [check_unsafety] unsafety-checking `::filter` +#3 [analysis] running analysis passes on this crate +end of query stack diff --git a/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_08_53-129174.txt b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_08_53-129174.txt new file mode 100644 index 0000000..de09993 --- /dev/null +++ b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_08_53-129174.txt @@ -0,0 +1,78 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + 0: 0x71463e17528f - std::backtrace_rs::backtrace::libunwind::trace::he4ee80166a02c846 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5 + 1: 0x71463e17528f - std::backtrace_rs::backtrace::trace_unsynchronized::h4665ca2a08e42cea + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 + 2: 0x71463e17528f - std::backtrace::Backtrace::create::h53f88232b3c879c4 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:331:13 + 3: 0x71463e1751d0 - std::backtrace::Backtrace::force_capture::h9de6994a0c478360 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:312:9 + 4: 0x7146410290b7 - std[e4dfbc2c3f4b09f1]::panicking::update_hook::>::{closure#0} + 5: 0x71463e18fac0 - as core::ops::function::Fn>::call::h022ca2c0d8c21c9e + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2034:9 + 6: 0x71463e18fac0 - std::panicking::rust_panic_with_hook::h0ad14d90dcf5224f + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:783:13 + 7: 0x714641051fa4 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic::::{closure#0} + 8: 0x71464104ed16 - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> + 9: 0x71464104e9f6 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic:: + 10: 0x71464105b451 - ::emit_producing_guarantee + 11: 0x7146414b3e51 - rustc_middle[fda44fdb505d3e7f]::util::bug::opt_span_bug_fmt::::{closure#0} + 12: 0x7146414973aa - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} + 13: 0x714641497228 - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> + 14: 0x71463f285df0 - rustc_middle[fda44fdb505d3e7f]::util::bug::bug_fmt + 15: 0x714642a390fe - >::fold_ty + 16: 0x71464297a3d0 - ::ctor_sub_tys + 17: 0x714642979fae - >::wild_from_ctor + 18: 0x714642965b6e - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 19: 0x7146429632c3 - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 20: 0x71464295293a - rustc_pattern_analysis[4bbf7d37c729d81f]::analyze_match + 21: 0x71463faa76ee - ::is_let_irrefutable + 22: 0x71463faa1e8d - ::visit_expr + 23: 0x71463faa12fd - ::visit_expr + 24: 0x71463faa1913 - ::visit_expr + 25: 0x71463faa12fd - ::visit_expr + 26: 0x71463faa23ee - ::visit_expr + 27: 0x71463faa12fd - ::visit_expr + 28: 0x7146429a778b - rustc_mir_build[95b41b8ff12a5765]::thir::pattern::check_match::check_match + 29: 0x7146429a7435 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 30: 0x714642918982 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 31: 0x714642918695 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace + 32: 0x71464299968b - rustc_mir_build[95b41b8ff12a5765]::build::mir_built + 33: 0x714642999595 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 34: 0x71464233deed - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 35: 0x71464233da4c - rustc_query_impl[e4152ad88c3d6c78]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace + 36: 0x714642995315 - rustc_mir_build[95b41b8ff12a5765]::check_unsafety::check_unsafety + 37: 0x714642995109 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 38: 0x714642994746 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 39: 0x71464299444f - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace + 40: 0x714642b11fbf - rustc_interface[ba2b6dc4c96cb491]::passes::analysis + 41: 0x714642b118e5 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 42: 0x714642f7f3a2 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 43: 0x714642f7f149 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 44: 0x714642dfc814 - rustc_interface[ba2b6dc4c96cb491]::interface::run_compiler::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0} + 45: 0x71464313eb6e - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>> + 46: 0x71464313e9ca - <::spawn_unchecked_, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#1} as core[836963c7c1decc11]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 47: 0x71463e199145 - as core::ops::function::FnOnce>::call_once::h19b9e642d37e7272 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 48: 0x71463e199145 - as core::ops::function::FnOnce>::call_once::h97265befc434d3ae + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 49: 0x71463e199145 - std::sys::pal::unix::thread::Thread::new::thread_start::h420dad5cf01a9f35 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys/pal/unix/thread.rs:108:17 + 50: 0x71463de94ac3 - start_thread + at ./nptl/pthread_create.c:442:8 + 51: 0x71463df26850 - __GI___clone3 + at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 + 52: 0x0 - + + +rustc version: 1.78.0 (9b00956e5 2024-04-29) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [check_match] match-checking `::filter` +#1 [mir_built] building MIR for `::filter` +#2 [check_unsafety] unsafety-checking `::filter` +#3 [analysis] running analysis passes on this crate +end of query stack diff --git a/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_11_44-129588.txt b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_11_44-129588.txt new file mode 100644 index 0000000..ea6572d --- /dev/null +++ b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_11_44-129588.txt @@ -0,0 +1,78 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + 0: 0x781af997528f - std::backtrace_rs::backtrace::libunwind::trace::he4ee80166a02c846 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5 + 1: 0x781af997528f - std::backtrace_rs::backtrace::trace_unsynchronized::h4665ca2a08e42cea + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 + 2: 0x781af997528f - std::backtrace::Backtrace::create::h53f88232b3c879c4 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:331:13 + 3: 0x781af99751d0 - std::backtrace::Backtrace::force_capture::h9de6994a0c478360 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:312:9 + 4: 0x781afc8290b7 - std[e4dfbc2c3f4b09f1]::panicking::update_hook::>::{closure#0} + 5: 0x781af998fac0 - as core::ops::function::Fn>::call::h022ca2c0d8c21c9e + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2034:9 + 6: 0x781af998fac0 - std::panicking::rust_panic_with_hook::h0ad14d90dcf5224f + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:783:13 + 7: 0x781afc851fa4 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic::::{closure#0} + 8: 0x781afc84ed16 - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> + 9: 0x781afc84e9f6 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic:: + 10: 0x781afc85b451 - ::emit_producing_guarantee + 11: 0x781afccb3e51 - rustc_middle[fda44fdb505d3e7f]::util::bug::opt_span_bug_fmt::::{closure#0} + 12: 0x781afcc973aa - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} + 13: 0x781afcc97228 - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> + 14: 0x781afaa85df0 - rustc_middle[fda44fdb505d3e7f]::util::bug::bug_fmt + 15: 0x781afe2390fe - >::fold_ty + 16: 0x781afe17a3d0 - ::ctor_sub_tys + 17: 0x781afe179fae - >::wild_from_ctor + 18: 0x781afe165b6e - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 19: 0x781afe1632c3 - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 20: 0x781afe15293a - rustc_pattern_analysis[4bbf7d37c729d81f]::analyze_match + 21: 0x781afb2a76ee - ::is_let_irrefutable + 22: 0x781afb2a1e8d - ::visit_expr + 23: 0x781afb2a12fd - ::visit_expr + 24: 0x781afb2a1913 - ::visit_expr + 25: 0x781afb2a12fd - ::visit_expr + 26: 0x781afb2a23ee - ::visit_expr + 27: 0x781afb2a12fd - ::visit_expr + 28: 0x781afe1a778b - rustc_mir_build[95b41b8ff12a5765]::thir::pattern::check_match::check_match + 29: 0x781afe1a7435 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 30: 0x781afe118982 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 31: 0x781afe118695 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace + 32: 0x781afe19968b - rustc_mir_build[95b41b8ff12a5765]::build::mir_built + 33: 0x781afe199595 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 34: 0x781afdb3deed - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 35: 0x781afdb3da4c - rustc_query_impl[e4152ad88c3d6c78]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace + 36: 0x781afe195315 - rustc_mir_build[95b41b8ff12a5765]::check_unsafety::check_unsafety + 37: 0x781afe195109 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 38: 0x781afe194746 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 39: 0x781afe19444f - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace + 40: 0x781afe311fbf - rustc_interface[ba2b6dc4c96cb491]::passes::analysis + 41: 0x781afe3118e5 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 42: 0x781afe77f3a2 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 43: 0x781afe77f149 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 44: 0x781afe5fc814 - rustc_interface[ba2b6dc4c96cb491]::interface::run_compiler::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0} + 45: 0x781afe93eb6e - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>> + 46: 0x781afe93e9ca - <::spawn_unchecked_, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#1} as core[836963c7c1decc11]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 47: 0x781af9999145 - as core::ops::function::FnOnce>::call_once::h19b9e642d37e7272 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 48: 0x781af9999145 - as core::ops::function::FnOnce>::call_once::h97265befc434d3ae + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 49: 0x781af9999145 - std::sys::pal::unix::thread::Thread::new::thread_start::h420dad5cf01a9f35 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys/pal/unix/thread.rs:108:17 + 50: 0x781af9694ac3 - start_thread + at ./nptl/pthread_create.c:442:8 + 51: 0x781af9726850 - __GI___clone3 + at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 + 52: 0x0 - + + +rustc version: 1.78.0 (9b00956e5 2024-04-29) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [check_match] match-checking `::filter` +#1 [mir_built] building MIR for `::filter` +#2 [check_unsafety] unsafety-checking `::filter` +#3 [analysis] running analysis passes on this crate +end of query stack diff --git a/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_11_57-129706.txt b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_11_57-129706.txt new file mode 100644 index 0000000..914ee96 --- /dev/null +++ b/container-chains/runtime-templates/simple/rustc-ice-2024-05-26T13_11_57-129706.txt @@ -0,0 +1,78 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + 0: 0x7ed92562728f - std::backtrace_rs::backtrace::libunwind::trace::he4ee80166a02c846 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5 + 1: 0x7ed92562728f - std::backtrace_rs::backtrace::trace_unsynchronized::h4665ca2a08e42cea + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 + 2: 0x7ed92562728f - std::backtrace::Backtrace::create::h53f88232b3c879c4 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:331:13 + 3: 0x7ed9256271d0 - std::backtrace::Backtrace::force_capture::h9de6994a0c478360 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/backtrace.rs:312:9 + 4: 0x7ed9224290b7 - std[e4dfbc2c3f4b09f1]::panicking::update_hook::>::{closure#0} + 5: 0x7ed925641ac0 - as core::ops::function::Fn>::call::h022ca2c0d8c21c9e + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2034:9 + 6: 0x7ed925641ac0 - std::panicking::rust_panic_with_hook::h0ad14d90dcf5224f + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:783:13 + 7: 0x7ed922451fa4 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic::::{closure#0} + 8: 0x7ed92244ed16 - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> + 9: 0x7ed92244e9f6 - std[e4dfbc2c3f4b09f1]::panicking::begin_panic:: + 10: 0x7ed92245b451 - ::emit_producing_guarantee + 11: 0x7ed9228b3e51 - rustc_middle[fda44fdb505d3e7f]::util::bug::opt_span_bug_fmt::::{closure#0} + 12: 0x7ed9228973aa - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} + 13: 0x7ed922897228 - rustc_middle[fda44fdb505d3e7f]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> + 14: 0x7ed920685df0 - rustc_middle[fda44fdb505d3e7f]::util::bug::bug_fmt + 15: 0x7ed923e390fe - >::fold_ty + 16: 0x7ed923d7a3d0 - ::ctor_sub_tys + 17: 0x7ed923d79fae - >::wild_from_ctor + 18: 0x7ed923d65b6e - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 19: 0x7ed923d632c3 - rustc_pattern_analysis[4bbf7d37c729d81f]::usefulness::compute_exhaustiveness_and_usefulness::::{closure#0} + 20: 0x7ed923d5293a - rustc_pattern_analysis[4bbf7d37c729d81f]::analyze_match + 21: 0x7ed920ea76ee - ::is_let_irrefutable + 22: 0x7ed920ea1e8d - ::visit_expr + 23: 0x7ed920ea12fd - ::visit_expr + 24: 0x7ed920ea1913 - ::visit_expr + 25: 0x7ed920ea12fd - ::visit_expr + 26: 0x7ed920ea23ee - ::visit_expr + 27: 0x7ed920ea12fd - ::visit_expr + 28: 0x7ed923da778b - rustc_mir_build[95b41b8ff12a5765]::thir::pattern::check_match::check_match + 29: 0x7ed923da7435 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 30: 0x7ed923d18982 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 31: 0x7ed923d18695 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace + 32: 0x7ed923d9968b - rustc_mir_build[95b41b8ff12a5765]::build::mir_built + 33: 0x7ed923d99595 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 34: 0x7ed92373deed - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 35: 0x7ed92373da4c - rustc_query_impl[e4152ad88c3d6c78]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace + 36: 0x7ed923d95315 - rustc_mir_build[95b41b8ff12a5765]::check_unsafety::check_unsafety + 37: 0x7ed923d95109 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 38: 0x7ed923d94746 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 39: 0x7ed923d9444f - rustc_query_impl[e4152ad88c3d6c78]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace + 40: 0x7ed923f11fbf - rustc_interface[ba2b6dc4c96cb491]::passes::analysis + 41: 0x7ed923f118e5 - rustc_query_impl[e4152ad88c3d6c78]::plumbing::__rust_begin_short_backtrace::> + 42: 0x7ed92437f3a2 - rustc_query_system[475239fef39bf53f]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e4152ad88c3d6c78]::plumbing::QueryCtxt, false> + 43: 0x7ed92437f149 - rustc_query_impl[e4152ad88c3d6c78]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 44: 0x7ed9241fc814 - rustc_interface[ba2b6dc4c96cb491]::interface::run_compiler::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0} + 45: 0x7ed92453eb6e - std[e4dfbc2c3f4b09f1]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>> + 46: 0x7ed92453e9ca - <::spawn_unchecked_, rustc_driver_impl[24a943716c49befe]::run_compiler::{closure#0}>::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[836963c7c1decc11]::result::Result<(), rustc_span[a4517f2b2e65298c]::ErrorGuaranteed>>::{closure#1} as core[836963c7c1decc11]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 47: 0x7ed92564b145 - as core::ops::function::FnOnce>::call_once::h19b9e642d37e7272 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 48: 0x7ed92564b145 - as core::ops::function::FnOnce>::call_once::h97265befc434d3ae + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/boxed.rs:2020:9 + 49: 0x7ed92564b145 - std::sys::pal::unix::thread::Thread::new::thread_start::h420dad5cf01a9f35 + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/sys/pal/unix/thread.rs:108:17 + 50: 0x7ed91f294ac3 - start_thread + at ./nptl/pthread_create.c:442:8 + 51: 0x7ed91f326850 - __GI___clone3 + at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 + 52: 0x0 - + + +rustc version: 1.78.0 (9b00956e5 2024-04-29) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [check_match] match-checking `::filter` +#1 [mir_built] building MIR for `::filter` +#2 [check_unsafety] unsafety-checking `::filter` +#3 [analysis] running analysis passes on this crate +end of query stack diff --git a/container-chains/runtime-templates/simple/src/lib.rs b/container-chains/runtime-templates/simple/src/lib.rs new file mode 100644 index 0000000..93c76fe --- /dev/null +++ b/container-chains/runtime-templates/simple/src/lib.rs @@ -0,0 +1,1203 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; +#[cfg(feature = "std")] +use sp_version::NativeVersion; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +pub mod migrations; +pub mod weights; + +pub use sp_runtime::{MultiAddress, Perbill, Permill}; +use { + cumulus_primitives_core::AggregateMessageOrigin, + dp_impl_tanssi_pallets_config::impl_tanssi_pallets_config, + frame_support::{ + construct_runtime, + dispatch::DispatchClass, + genesis_builder_helper::{build_config, create_default_config}, + pallet_prelude::DispatchResult, + parameter_types, + traits::{ + ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Contains, InsideBoth, InstanceFilter, + }, + weights::{ + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, + WEIGHT_REF_TIME_PER_SECOND, + }, + ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, + }, + }, + frame_system::{ + limits::{BlockLength, BlockWeights}, + EnsureRoot, + }, + nimbus_primitives::{NimbusId, SlotBeacon}, + pallet_transaction_payment::CurrencyAdapter, + parity_scale_codec::{Decode, Encode}, + polkadot_runtime_common::SlowAdjustingFeeUpdate, + scale_info::TypeInfo, + smallvec::smallvec, + sp_api::impl_runtime_apis, + sp_consensus_slots::{Slot, SlotDuration}, + sp_core::{MaxEncodedLen, OpaqueMetadata}, + sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, + }, + sp_std::prelude::*, + sp_version::RuntimeVersion, +}; + +pub mod xcm_config; + +// Polkadot imports +use polkadot_runtime_common::BlockHashCount; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Balance of an account. +pub type Balance = u128; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// An index to a block. +pub type BlockNumber = u32; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +/// Block header type as expected by this runtime. +pub type Header = generic::Header; + +/// Block type as expected by this runtime. +pub type Block = generic::Block; + +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; + +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); + +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; + +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +pub mod currency { + use super::Balance; + + pub const MICROUNIT: Balance = 1_000_000; + pub const MILLIUNIT: Balance = 1_000_000_000; + pub const UNIT: Balance = 1_000_000_000_000; + pub const KILOUNIT: Balance = 1_000_000_000_000_000; + + pub const STORAGE_BYTE_FEE: Balance = 100 * MICROUNIT; + + pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 100 * MILLIUNIT + (bytes as Balance) * STORAGE_BYTE_FEE + } +} + +/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the +/// node's balance type. +/// +/// This should typically create a mapping between the following ranges: +/// - `[0, MAXIMUM_BLOCK_WEIGHT]` +/// - `[Balance::min, Balance::max]` +/// +/// Yet, it can be used for any other sort of change to weight-fee. Some examples being: +/// - Setting it to `0` will essentially disable the weight fee. +/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients { + // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT: + // in our template, we map to 1/10 of that, or 1/10 MILLIUNIT + let p = MILLIUNIT / 10; + let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); + smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } +} + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use { + super::*, + sp_runtime::{generic, traits::BlakeTwo256}, + }; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +impl_opaque_keys! { + pub struct SessionKeys { } +} + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("container-chain-template"), + impl_name: create_runtime_str!("container-chain-template"), + authoring_version: 1, + spec_version: 700, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// This determines the average expected block time that we are targeting. +/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. +/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked +/// up by `pallet_aura` to implement `fn slot_duration()`. +/// +/// Change this to adjust the block time. +pub const MILLISECS_PER_BLOCK: u64 = 6000; + +// NOTE: Currently it is not possible to change the slot duration after the chain has started. +// Attempting to do so will brick block production. +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +// Time is measured by number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; + +pub const SUPPLY_FACTOR: Balance = 100; + +// Unit = the base number of indivisible units for balances +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = 1_000_000_000; +pub const MICROUNIT: Balance = 1_000_000; + +pub const STORAGE_BYTE_FEE: Balance = 100 * MICROUNIT * SUPPLY_FACTOR; + +pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 100 * MILLIUNIT * SUPPLY_FACTOR + (bytes as Balance) * STORAGE_BYTE_FEE +} + +/// The existential deposit. Set to 1/10 of the Connected Relay Chain. +pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; + +/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is +/// used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); + +/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used by +/// `Operational` extrinsics. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// We allow for 0.5 of a second of compute with a 12 second average block time. +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + + // This part is copied from Substrate's `bin/node/runtime/src/lib.rs`. + // The `RuntimeBlockLength` and `RuntimeBlockWeights` exist here because the + // `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize + // the lazy contract deletion. + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have some extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); + pub const SS58Prefix: u16 = 42; +} + +// Configure FRAME pallets to include in runtime. + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Nonce = Index; + /// The index type for blocks. + type Block = Block; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// Converts a module to an index of this module in the runtime. + type PalletInfo = PalletInfo; + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// The basic call filter to use in dispatchable. + type BaseCallFilter = InsideBoth; + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = weights::frame_system::SubstrateWeight; + /// Block & extrinsics weights: base values and limits. + type BlockWeights = RuntimeBlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = RuntimeBlockLength; + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The action to take on a Runtime Upgrade + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = RuntimeTask; +} + +parameter_types! { + pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = ConstU32<50>; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = RuntimeFreezeReason; + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxHolds = ConstU32<0>; + type WeightInfo = weights::pallet_balances::SubstrateWeight; +} + +parameter_types! { + pub const TransactionByteFee: Balance = 1; +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // This will burn the fees + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; +pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; +pub const BLOCK_PROCESSING_VELOCITY: u32 = 1; + +type ConsensusHook = pallet_async_backing::consensus_hook::FixedVelocityConsensusHook< + Runtime, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + +impl cumulus_pallet_parachain_system::Config for Runtime { + type WeightInfo = weights::cumulus_pallet_parachain_system::SubstrateWeight; + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type OutboundXcmpMessageSource = XcmpQueue; + type SelfParaId = parachain_info::Pallet; + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type ReservedDmpWeight = ReservedDmpWeight; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; +} + +pub struct ParaSlotProvider; +impl sp_core::Get<(Slot, SlotDuration)> for ParaSlotProvider { + fn get() -> (Slot, SlotDuration) { + let slot = u64::from(::SlotBeacon::slot()); + (Slot::from(slot), SlotDuration::from_millis(SLOT_DURATION)) + } +} + +parameter_types! { + pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; +} + +impl pallet_async_backing::Config for Runtime { + type AllowMultipleBlocksPerSlot = ConstBool; + type GetAndVerifySlot = + pallet_async_backing::ParaSlot; + type ExpectedBlockTime = ExpectedBlockTime; +} + +impl parachain_info::Config for Runtime {} + +parameter_types! { + pub const Period: u32 = 6 * HOURS; + pub const Offset: u32 = 0; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_sudo::SubstrateWeight; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = weights::pallet_utility::SubstrateWeight; +} + +/// The type used to represent the kinds of proxying allowed. +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, MaxEncodedLen, TypeInfo, +)] +#[allow(clippy::unnecessary_cast)] +pub enum ProxyType { + /// All calls can be proxied. This is the trivial/most permissive filter. + Any = 0, + /// Only extrinsics that do not transfer funds. + NonTransfer = 1, + /// Only extrinsics related to governance (democracy and collectives). + Governance = 2, + /// Allow to veto an announced proxy call. + CancelProxy = 3, + /// Allow extrinsic related to Balances. + Balances = 4, +} + +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + // Since proxy filters are respected in all dispatches of the Utility + // pallet, it should never need to be filtered by any proxy. + if let RuntimeCall::Utility(..) = c { + return true; + } + + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => { + matches!( + c, + RuntimeCall::System(..) + | RuntimeCall::ParachainSystem(..) + | RuntimeCall::Timestamp(..) + | RuntimeCall::Proxy(..) + ) + } + // We don't have governance yet + ProxyType::Governance => false, + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) + ), + ProxyType::Balances => { + matches!(c, RuntimeCall::Balances(..)) + } + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + _ => false, + } + } +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + // One storage item; key size 32, value size 8 + type ProxyDepositBase = ConstU128<{ deposit(1, 8) }>; + // Additional storage item size of 33 bytes (32 bytes AccountId + 1 byte sizeof(ProxyType)). + type ProxyDepositFactor = ConstU128<{ deposit(0, 33) }>; + type MaxProxies = ConstU32<32>; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU128<{ deposit(1, 8) }>; + // Additional storage item size of 68 bytes: + // - 32 bytes AccountId + // - 32 bytes Hasher (Blake2256) + // - 4 bytes BlockNumber (u32) + type AnnouncementDepositFactor = ConstU128<{ deposit(0, 68) }>; + type WeightInfo = weights::pallet_proxy::SubstrateWeight; +} + +pub struct XcmExecutionManager; +impl xcm_primitives::PauseXcmExecution for XcmExecutionManager { + fn suspend_xcm_execution() -> DispatchResult { + XcmpQueue::suspend_xcm_execution(RuntimeOrigin::root()) + } + fn resume_xcm_execution() -> DispatchResult { + XcmpQueue::resume_xcm_execution(RuntimeOrigin::root()) + } +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MigrationsList = (migrations::TemplateMigrations,); + type XcmExecutionManager = XcmExecutionManager; +} + +/// Maintenance mode Call filter +pub struct MaintenanceFilter; +impl Contains for MaintenanceFilter { + fn contains(c: &RuntimeCall) -> bool { + !matches!(c, RuntimeCall::Balances(_) | RuntimeCall::PolkadotXcm(_)) + } +} + +/// Normal Call Filter +/// We dont allow to create nor mint assets, this for now is disabled +/// We only allow transfers. For now creation of assets will go through +/// asset-manager, while minting/burning only happens through xcm messages +/// This can change in the future +pub struct NormalFilter; +impl Contains for NormalFilter { + fn contains(_c: &RuntimeCall) -> bool { + true + } +} + +impl pallet_maintenance_mode::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type NormalCallFilter = NormalFilter; + type MaintenanceCallFilter = MaintenanceFilter; + type MaintenanceOrigin = EnsureRoot; + type XcmExecutionManager = XcmExecutionManager; +} + +impl pallet_root_testing::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_tx_pause::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PauseOrigin = EnsureRoot; + type UnpauseOrigin = EnsureRoot; + type WhitelistedCalls = (); + type MaxNameLen = ConstU32<256>; + type WeightInfo = weights::pallet_tx_pause::SubstrateWeight; +} + +impl dp_impl_tanssi_pallets_config::Config for Runtime { + const SLOT_DURATION: u64 = SLOT_DURATION; + type TimestampWeights = weights::pallet_timestamp::SubstrateWeight; + type AuthorInherentWeights = weights::pallet_author_inherent::SubstrateWeight; + type AuthoritiesNotingWeights = weights::pallet_cc_authorities_noting::SubstrateWeight; +} + +parameter_types! { + // One storage item; key size 32; value is size 4+4+16+32. Total = 1 * (32 + 56) + pub const DepositBase: Balance = currency::deposit(1, 88); + // Additional storage item size of 32 bytes. + pub const DepositFactor: Balance = currency::deposit(0, 32); + pub const MaxSignatories: u32 = 100; +} + +impl pallet_multisig::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type DepositBase = DepositBase; + type DepositFactor = DepositFactor; + type MaxSignatories = MaxSignatories; + type WeightInfo = weights::pallet_multisig::SubstrateWeight; +} + +impl pallet_template::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_template::weights::SubstrateWeight; +} + +impl pallet_insecure_randomness_collective_flip::Config for Runtime {} + +impl pallet_sortition_sum_game::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_sortition_sum_game::weights::SubstrateWeight; +} + +impl pallet_schelling_game_shared::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_schelling_game_shared::weights::SubstrateWeight; + type Currency = Balances; + type RandomnessSource = RandomnessCollectiveFlip; + type Slash = (); + type Reward = (); + type SortitionSumGameSource = SortitionSumGame; +} + +impl pallet_profile_validation::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_profile_validation::weights::SubstrateWeight; + type Currency = Balances; + type SchellingGameSharedSource = SchellingGameShared; + type Slash = (); + type Reward = (); +} + +impl pallet_shared_storage::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_shared_storage::weights::SubstrateWeight; +} + +impl pallet_positive_externality::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_positive_externality::weights::SubstrateWeight; + type SharedStorageSource = SharedStorage; + type Currency = Balances; + type SchellingGameSharedSource = SchellingGameShared; +} + +impl pallet_department_funding::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_department_funding::weights::SubstrateWeight; + type SharedStorageSource = SharedStorage; + type Currency = Balances; + type SchellingGameSharedSource = SchellingGameShared; +} + +impl pallet_project_tips::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_project_tips::weights::SubstrateWeight; + type SharedStorageSource = SharedStorage; + type Currency = Balances; + type Reward = (); + type SchellingGameSharedSource = SchellingGameShared; +} + +impl_tanssi_pallets_config!(Runtime); + +// Create the runtime by composing the FRAME pallets that were previously configured. +construct_runtime!( + pub enum Runtime + { + // System support stuff. + System: frame_system = 0, + ParachainSystem: cumulus_pallet_parachain_system = 1, + Timestamp: pallet_timestamp = 2, + ParachainInfo: parachain_info = 3, + Sudo: pallet_sudo = 4, + Utility: pallet_utility = 5, + Proxy: pallet_proxy = 6, + Migrations: pallet_migrations = 7, + MaintenanceMode: pallet_maintenance_mode = 8, + TxPause: pallet_tx_pause = 9, + + // Monetary stuff. + Balances: pallet_balances = 10, + TransactionPayment: pallet_transaction_payment = 11, + + // Other utilities + Multisig: pallet_multisig = 16, + + // ContainerChain Author Verification + AuthoritiesNoting: pallet_cc_authorities_noting = 50, + AuthorInherent: pallet_author_inherent = 51, + + // XCM + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Storage, Event} = 70, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 71, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 72, + PolkadotXcm: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 73, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 74, + ForeignAssets: pallet_assets::::{Pallet, Call, Storage, Event} = 75, + ForeignAssetsCreator: pallet_foreign_asset_creator::{Pallet, Call, Storage, Event} = 76, + AssetRate: pallet_asset_rate::{Pallet, Call, Storage, Event} = 77, + XcmExecutorUtils: pallet_xcm_executor_utils::{Pallet, Call, Storage, Event} = 78, + + RootTesting: pallet_root_testing = 100, + AsyncBacking: pallet_async_backing::{Pallet, Storage} = 110, + + TemplateModule: pallet_template = 200, + SortitionSumGame: pallet_sortition_sum_game = 201, + SchellingGameShared: pallet_schelling_game_shared = 202, + ProfileValidation: pallet_profile_validation = 203, + SharedStorage: pallet_shared_storage = 204, + PositiveExternality: pallet_positive_externality = 205, + DepartmentFunding: pallet_department_funding = 206, + ProjectTips: pallet_project_tips = 207, + RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip = 208, + + + } +); + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_benchmarking::define_benchmarks!( + [frame_system, frame_system_benchmarking::Pallet::] + [cumulus_pallet_parachain_system, ParachainSystem] + [pallet_timestamp, Timestamp] + [pallet_sudo, Sudo] + [pallet_utility, Utility] + [pallet_proxy, Proxy] + [pallet_tx_pause, TxPause] + [pallet_balances, Balances] + [pallet_multisig, Multisig] + [pallet_cc_authorities_noting, AuthoritiesNoting] + [pallet_author_inherent, AuthorInherent] + [cumulus_pallet_xcmp_queue, XcmpQueue] + [cumulus_pallet_dmp_queue, DmpQueue] + [pallet_xcm, PalletXcmExtrinsicsBenchmark::] + [pallet_xcm_benchmarks::generic, pallet_xcm_benchmarks::generic::Pallet::] + [pallet_message_queue, MessageQueue] + [pallet_assets, ForeignAssets] + [pallet_foreign_asset_creator, ForeignAssetsCreator] + [pallet_asset_rate, AssetRate] + [pallet_xcm_executor_utils, XcmExecutorUtils] + ); +} + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl async_backing_primitives::UnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: async_backing_primitives::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn create_default_config() -> Vec { + create_default_config::() + } + + fn build_config(config: Vec) -> sp_genesis_builder::Result { + build_config::(config) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata( + extra: bool, + ) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig, + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{BenchmarkBatch, Benchmarking, BenchmarkError}; + use sp_core::storage::TrackedStorageKey; + use staging_xcm::latest::prelude::*; + impl frame_system_benchmarking::Config for Runtime { + fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { + ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); + Ok(()) + } + + fn verify_set_code() { + System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); + } + } + use crate::xcm_config::SelfReserve; + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + SelfReserve::get(), + ExistentialDeposit::get() + ).into()); + } + + impl pallet_xcm_benchmarks::Config for Runtime { + type XcmConfig = xcm_config::XcmConfig; + type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn valid_destination() -> Result { + Ok(MultiLocation::parent()) + } + fn worst_case_holding(_depositable_count: u32) -> MultiAssets { + // We only care for native asset until we support others + // TODO: refactor this case once other assets are supported + vec![MultiAsset{ + id: Concrete(MultiLocation::here()), + fun: Fungible(u128::MAX), + }].into() + } + } + + impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; + type RuntimeCall = RuntimeCall; + + fn worst_case_response() -> (u64, Response) { + (0u64, Response::Version(Default::default())) + } + + fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result<(MultiLocation, Junction), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { + Ok((MultiLocation::parent(), frame_system::Call::remark_with_event { remark: vec![] }.into())) + } + + fn subscribe_origin() -> Result { + Ok(MultiLocation::parent()) + } + + fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError> { + let origin = MultiLocation::parent(); + let assets: MultiAssets = (Concrete(MultiLocation::parent()), 1_000u128).into(); + let ticket = MultiLocation { parents: 0, interior: Here }; + Ok((origin, ticket, assets)) + } + + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn alias_origin() -> Result<(MultiLocation, MultiLocation), BenchmarkError> { + Err(BenchmarkError::Skip) + } + } + + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; + impl pallet_xcm::benchmarking::Config for Runtime { + fn reachable_dest() -> Option { + Some(Parent.into()) + } + + fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> { + // Relay/native token can be teleported between AH and Relay. + Some(( + MultiAsset { + fun: Fungible(EXISTENTIAL_DEPOSIT), + id: Concrete(Parent.into()) + }, + Parent.into(), + )) + } + + fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> { + use xcm_config::SelfReserve; + // AH can reserve transfer native token to some random parachain. + let random_para_id = 43211234; + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( + random_para_id.into() + ); + Some(( + MultiAsset { + fun: Fungible(EXISTENTIAL_DEPOSIT), + id: Concrete(SelfReserve::get()) + }, + ParentThen(Parachain(random_para_id).into()).into(), + )) + } + + fn set_up_complex_asset_transfer( + ) -> Option<(MultiAssets, u32, MultiLocation, Box)> { + use xcm_config::SelfReserve; + // Transfer to Relay some local AH asset (local-reserve-transfer) while paying + // fees using teleported native token. + // (We don't care that Relay doesn't accept incoming unknown AH local asset) + let dest = Parent.into(); + + let fee_amount = EXISTENTIAL_DEPOSIT; + let fee_asset: MultiAsset = (SelfReserve::get(), fee_amount).into(); + + let who = frame_benchmarking::whitelisted_caller(); + // Give some multiple of the existential deposit + let balance = fee_amount + EXISTENTIAL_DEPOSIT * 1000; + let _ = >::make_free_balance_be( + &who, balance, + ); + + // verify initial balance + assert_eq!(Balances::free_balance(&who), balance); + + // set up local asset + let asset_amount = 10u128; + let initial_asset_amount = asset_amount * 10; + + let (asset_id, asset_location) = pallet_foreign_asset_creator::benchmarks::create_default_minted_asset::( + initial_asset_amount, + who.clone() + ); + + let transfer_asset: MultiAsset = (asset_location, asset_amount).into(); + + let assets: MultiAssets = vec![fee_asset.clone(), transfer_asset].into(); + let fee_index = if assets.get(0).unwrap().eq(&fee_asset) { 0 } else { 1 }; + + // verify transferred successfully + let verify = Box::new(move || { + // verify native balance after transfer, decreased by transferred fee amount + // (plus transport fees) + assert!(Balances::free_balance(&who) <= balance - fee_amount); + // verify asset balance decreased by exactly transferred amount + assert_eq!( + ForeignAssets::balance(asset_id, &who), + initial_asset_amount - asset_amount, + ); + }); + Some((assets, fee_index as u32, dest, verify)) + } + } + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac") + .to_vec() + .into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80") + .to_vec() + .into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a") + .to_vec() + .into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850") + .to_vec() + .into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7") + .to_vec() + .into(), + // The transactional storage limit. + hex_literal::hex!("3a7472616e73616374696f6e5f6c6576656c3a") + .to_vec() + .into(), + + // ParachainInfo ParachainId + hex_literal::hex!( "0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f") + .to_vec() + .into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmarks!(params, batches); + + Ok(batches) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + let weight = Executive::try_runtime_upgrade(checks).unwrap(); + (weight, RuntimeBlockWeights::get().max_block) + } + + fn execute_block( + block: Block, + state_root_check: bool, + signature_check: bool, + select: frame_try_runtime::TryStateSelect, + ) -> Weight { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. + Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl dp_slot_duration_runtime_api::TanssiSlotDurationApi for Runtime { + fn slot_duration() -> u64 { + SLOT_DURATION + } + } +} + +#[allow(dead_code)] +struct CheckInherents; + +#[allow(deprecated)] +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + CheckInherents = CheckInherents, + BlockExecutor = pallet_author_inherent::BlockExecutor::, +} diff --git a/container-chains/runtime-templates/simple/src/migrations.rs b/container-chains/runtime-templates/simple/src/migrations.rs new file mode 100644 index 0000000..36c6445 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/migrations.rs @@ -0,0 +1,58 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Migrations +//! +//! This module acts as a registry where each migration is defined. Each migration should implement +//! the "Migration" trait declared in the pallet-migrations crate. + +use { + frame_support::{pallet_prelude::GetStorageVersion, traits::PalletInfoAccess}, + pallet_migrations::{GetMigrations, Migration}, + runtime_common::migrations::{ + PolkadotXcmMigrationFixVersion, XcmpQueueMigrationFixVersion, XcmpQueueMigrationV3, + XcmpQueueMigrationV4, + }, + sp_std::{marker::PhantomData, prelude::*}, +}; + +pub struct TemplateMigrations( + PhantomData<(Runtime, XcmpQueue, PolkadotXcm)>, +); + +impl GetMigrations + for TemplateMigrations +where + PolkadotXcm: GetStorageVersion + PalletInfoAccess + 'static, + XcmpQueue: GetStorageVersion + PalletInfoAccess + 'static, + Runtime: frame_system::Config, + Runtime: cumulus_pallet_xcmp_queue::Config, +{ + fn get_migrations() -> Vec> { + let migrate_polkadot_xcm_v1 = + PolkadotXcmMigrationFixVersion::(Default::default()); + let migrate_xcmp_queue_v2 = + XcmpQueueMigrationFixVersion::(Default::default()); + let migrate_xcmp_queue_v3 = XcmpQueueMigrationV3::(Default::default()); + let migrate_xcmp_queue_v4 = XcmpQueueMigrationV4::(Default::default()); + vec![ + Box::new(migrate_polkadot_xcm_v1), + Box::new(migrate_xcmp_queue_v2), + Box::new(migrate_xcmp_queue_v3), + Box::new(migrate_xcmp_queue_v4), + ] + } +} diff --git a/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_dmp_queue.rs b/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_dmp_queue.rs new file mode 100644 index 0000000..4c9f704 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_dmp_queue.rs @@ -0,0 +1,133 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for cumulus_pallet_dmp_queue +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// cumulus_pallet_dmp_queue +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/cumulus_pallet_dmp_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for cumulus_pallet_dmp_queue using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl cumulus_pallet_dmp_queue::WeightInfo for SubstrateWeight { + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn on_idle_good_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65661` + // Estimated: `69126` + // Minimum execution time: 129_008_000 picoseconds. + Weight::from_parts(130_461_000, 69126) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca7d95d3e948effbeccff2de2c182672836` (r:1 w:1) + fn on_idle_large_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65660` + // Estimated: `69125` + // Minimum execution time: 73_402_000 picoseconds. + Weight::from_parts(74_520_000, 69125) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn on_idle_overweight_good_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65691` + // Estimated: `69156` + // Minimum execution time: 120_484_000 picoseconds. + Weight::from_parts(121_449_000, 69156) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `DmpQueue::MigrationStatus` (r:1 w:1) + /// Proof: `DmpQueue::MigrationStatus` (`max_values`: Some(1), `max_size`: Some(1028), added: 1523, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca754904d6d8c6fe06c4e5965f9b8397421` (r:1 w:0) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca70f923ef3252d0166429d36d20ed665a8` (r:1 w:1) + /// Storage: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xcd5c1f6df63bc97f4a8ce37f14a50ca772275f64c354954352b71eea39cfaca2` (r:1 w:1) + fn on_idle_overweight_large_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65690` + // Estimated: `69155` + // Minimum execution time: 64_249_000 picoseconds. + Weight::from_parts(65_501_000, 69155) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_parachain_system.rs b/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_parachain_system.rs new file mode 100644 index 0000000..13a8d2c --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_parachain_system.rs @@ -0,0 +1,80 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for cumulus_pallet_parachain_system +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// cumulus_pallet_parachain_system +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/cumulus_pallet_parachain_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for cumulus_pallet_parachain_system using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl cumulus_pallet_parachain_system::WeightInfo for SubstrateWeight { + /// Storage: `ParachainSystem::LastDmqMqcHead` (r:1 w:1) + /// Proof: `ParachainSystem::LastDmqMqcHead` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ProcessedDownwardMessages` (r:0 w:1) + /// Proof: `ParachainSystem::ProcessedDownwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1000) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 1000]`. + fn enqueue_inbound_downward_messages(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `12` + // Estimated: `3517` + // Minimum execution time: 3_065_000 picoseconds. + Weight::from_parts(3_180_000, 3517) + // Standard Error: 19_687 + .saturating_add(Weight::from_parts(192_303_686, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_xcmp_queue.rs b/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_xcmp_queue.rs new file mode 100644 index 0000000..750ae38 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/cumulus_pallet_xcmp_queue.rs @@ -0,0 +1,152 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for cumulus_pallet_xcmp_queue +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// cumulus_pallet_xcmp_queue +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/cumulus_pallet_xcmp_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for cumulus_pallet_xcmp_queue using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl cumulus_pallet_xcmp_queue::WeightInfo for SubstrateWeight { + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:1) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_config_with_u32() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `1594` + // Minimum execution time: 6_558_000 picoseconds. + Weight::from_parts(6_698_000, 1594) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:0) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn enqueue_xcmp_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `115` + // Estimated: `3517` + // Minimum execution time: 16_071_000 picoseconds. + Weight::from_parts(16_550_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn suspend_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `1594` + // Minimum execution time: 4_075_000 picoseconds. + Weight::from_parts(4_200_000, 1594) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn resume_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `1629` + // Minimum execution time: 5_006_000 picoseconds. + Weight::from_parts(5_146_000, 1629) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn take_first_concatenated_xcm() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 69_921_000 picoseconds. + Weight::from_parts(70_110_000, 0) + } + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:0) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn on_idle_good_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65744` + // Estimated: `69209` + // Minimum execution time: 117_577_000 picoseconds. + Weight::from_parts(118_802_000, 69209) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + fn on_idle_large_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65743` + // Estimated: `69208` + // Minimum execution time: 55_910_000 picoseconds. + Weight::from_parts(57_425_000, 69208) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/frame_system.rs b/container-chains/runtime-templates/simple/src/weights/frame_system.rs new file mode 100644 index 0000000..1fb2b3e --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/frame_system.rs @@ -0,0 +1,185 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for frame_system +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// frame_system +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/frame_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for frame_system using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl frame_system::WeightInfo for SubstrateWeight { + /// The range of component `b` is `[0, 3932160]`. + fn remark(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_844_000 picoseconds. + Weight::from_parts(1_305_052, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(377, 0).saturating_mul(b.into())) + } + /// The range of component `b` is `[0, 3932160]`. + fn remark_with_event(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_320_000 picoseconds. + Weight::from_parts(7_556_000, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_723, 0).saturating_mul(b.into())) + } + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + fn set_heap_pages() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1485` + // Minimum execution time: 4_527_000 picoseconds. + Weight::from_parts(4_768_000, 1485) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `127` + // Estimated: `1612` + // Minimum execution time: 142_248_334_000 picoseconds. + Weight::from_parts(144_262_006_000, 1612) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn set_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_776_000 picoseconds. + Weight::from_parts(2_861_000, 0) + // Standard Error: 2_142 + .saturating_add(Weight::from_parts(937_527, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn kill_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_893_000 picoseconds. + Weight::from_parts(2_977_000, 0) + // Standard Error: 1_057 + .saturating_add(Weight::from_parts(660_862, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `p` is `[0, 1000]`. + fn kill_prefix(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `64 + p * (69 ±0)` + // Estimated: `74 + p * (70 ±0)` + // Minimum execution time: 5_050_000 picoseconds. + Weight::from_parts(5_212_000, 74) + // Standard Error: 1_660 + .saturating_add(Weight::from_parts(1_214_878, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) + } + /// Storage: `System::AuthorizedUpgrade` (r:0 w:1) + /// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + fn authorize_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 16_386_000 picoseconds. + Weight::from_parts(19_658_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::AuthorizedUpgrade` (r:1 w:1) + /// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn apply_authorized_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `149` + // Estimated: `1634` + // Minimum execution time: 147_587_967_000 picoseconds. + Weight::from_parts(149_640_280_000, 1634) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/mod.rs b/container-chains/runtime-templates/simple/src/weights/mod.rs new file mode 100644 index 0000000..3bc8f23 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/mod.rs @@ -0,0 +1,39 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! A list of the different weight modules for our runtime. + +pub mod cumulus_pallet_dmp_queue; +pub mod cumulus_pallet_parachain_system; +pub mod cumulus_pallet_xcmp_queue; +pub mod frame_system; +pub mod pallet_asset_rate; +pub mod pallet_assets; +pub mod pallet_author_inherent; +pub mod pallet_balances; +pub mod pallet_cc_authorities_noting; +pub mod pallet_foreign_asset_creator; +pub mod pallet_message_queue; +pub mod pallet_multisig; +pub mod pallet_proxy; + +pub mod pallet_sudo; +pub mod pallet_timestamp; +pub mod pallet_tx_pause; +pub mod pallet_utility; +pub mod pallet_xcm; +pub mod pallet_xcm_executor_utils; +pub mod xcm; diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_asset_rate.rs b/container-chains/runtime-templates/simple/src/weights/pallet_asset_rate.rs new file mode 100644 index 0000000..7b5fd4f --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_asset_rate.rs @@ -0,0 +1,90 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_asset_rate +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_asset_rate +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_asset_rate.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_asset_rate using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_asset_rate::WeightInfo for SubstrateWeight { + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(34), added: 2509, mode: `MaxEncodedLen`) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3499` + // Minimum execution time: 12_989_000 picoseconds. + Weight::from_parts(13_287_000, 3499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(34), added: 2509, mode: `MaxEncodedLen`) + fn update() -> Weight { + // Proof Size summary in bytes: + // Measured: `135` + // Estimated: `3499` + // Minimum execution time: 13_176_000 picoseconds. + Weight::from_parts(13_581_000, 3499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(34), added: 2509, mode: `MaxEncodedLen`) + fn remove() -> Weight { + // Proof Size summary in bytes: + // Measured: `135` + // Estimated: `3499` + // Minimum execution time: 13_845_000 picoseconds. + Weight::from_parts(14_318_000, 3499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_assets.rs b/container-chains/runtime-templates/simple/src/weights/pallet_assets.rs new file mode 100644 index 0000000..10c3c50 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_assets.rs @@ -0,0 +1,489 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_assets +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_assets.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_assets::WeightInfo for SubstrateWeight { + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(0, 0) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn force_create() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3673` + // Minimum execution time: 13_199_000 picoseconds. + Weight::from_parts(13_512_000, 3673) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn start_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3673` + // Minimum execution time: 12_863_000 picoseconds. + Weight::from_parts(13_409_000, 3673) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1001 w:1000) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1000 w:1000) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn destroy_accounts(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `79 + c * (208 ±0)` + // Estimated: `3673 + c * (2607 ±0)` + // Minimum execution time: 18_413_000 picoseconds. + Weight::from_parts(18_684_000, 3673) + // Standard Error: 9_946 + .saturating_add(Weight::from_parts(15_695_085, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2607).saturating_mul(c.into())) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1001 w:1000) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 1000]`. + fn destroy_approvals(a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `408 + a * (86 ±0)` + // Estimated: `3673 + a * (2621 ±0)` + // Minimum execution time: 18_724_000 picoseconds. + Weight::from_parts(19_023_000, 3673) + // Standard Error: 7_058 + .saturating_add(Weight::from_parts(5_980_169, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_parts(0, 2621).saturating_mul(a.into())) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:0) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn finish_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 14_943_000 picoseconds. + Weight::from_parts(15_447_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 27_098_000 picoseconds. + Weight::from_parts(28_216_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + fn burn() -> Weight { + // Proof Size summary in bytes: + // Measured: `345` + // Estimated: `3673` + // Minimum execution time: 35_251_000 picoseconds. + Weight::from_parts(35_675_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `384` + // Estimated: `6204` + // Minimum execution time: 50_179_000 picoseconds. + Weight::from_parts(51_178_000, 6204) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `384` + // Estimated: `6204` + // Minimum execution time: 44_753_000 picoseconds. + Weight::from_parts(45_704_000, 6204) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `384` + // Estimated: `6204` + // Minimum execution time: 50_560_000 picoseconds. + Weight::from_parts(51_366_000, 6204) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + fn freeze() -> Weight { + // Proof Size summary in bytes: + // Measured: `345` + // Estimated: `3673` + // Minimum execution time: 17_350_000 picoseconds. + Weight::from_parts(17_867_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + fn thaw() -> Weight { + // Proof Size summary in bytes: + // Measured: `345` + // Estimated: `3673` + // Minimum execution time: 17_511_000 picoseconds. + Weight::from_parts(18_054_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn freeze_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3673` + // Minimum execution time: 12_647_000 picoseconds. + Weight::from_parts(12_902_000, 3673) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn thaw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3673` + // Minimum execution time: 12_595_000 picoseconds. + Weight::from_parts(12_913_000, 3673) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:0) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn transfer_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 15_644_000 picoseconds. + Weight::from_parts(15_999_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn set_team() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 13_950_000 picoseconds. + Weight::from_parts(14_216_000, 3673) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 15_541_000 picoseconds. + Weight::from_parts(16_421_379, 3673) + // Standard Error: 364 + .saturating_add(Weight::from_parts(1_276, 0).saturating_mul(n.into())) + // Standard Error: 364 + .saturating_add(Weight::from_parts(1_244, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `401` + // Estimated: `3673` + // Minimum execution time: 16_208_000 picoseconds. + Weight::from_parts(16_525_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn force_set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `78` + // Estimated: `3673` + // Minimum execution time: 14_882_000 picoseconds. + Weight::from_parts(15_536_893, 3673) + // Standard Error: 332 + .saturating_add(Weight::from_parts(1_066, 0).saturating_mul(n.into())) + // Standard Error: 332 + .saturating_add(Weight::from_parts(1_019, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Metadata` (r:1 w:1) + /// Proof: `ForeignAssets::Metadata` (`max_values`: None, `max_size`: Some(138), added: 2613, mode: `MaxEncodedLen`) + fn force_clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `401` + // Estimated: `3673` + // Minimum execution time: 15_909_000 picoseconds. + Weight::from_parts(16_540_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn force_asset_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 13_304_000 picoseconds. + Weight::from_parts(13_732_000, 3673) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + fn approve_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3673` + // Minimum execution time: 20_775_000 picoseconds. + Weight::from_parts(21_281_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `552` + // Estimated: `6204` + // Minimum execution time: 61_759_000 picoseconds. + Weight::from_parts(62_823_000, 6204) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + fn cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `441` + // Estimated: `3673` + // Minimum execution time: 22_953_000 picoseconds. + Weight::from_parts(23_377_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Approvals` (r:1 w:1) + /// Proof: `ForeignAssets::Approvals` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + fn force_cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `441` + // Estimated: `3673` + // Minimum execution time: 23_307_000 picoseconds. + Weight::from_parts(23_840_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 14_539_000 picoseconds. + Weight::from_parts(14_974_000, 3673) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 20_725_000 picoseconds. + Weight::from_parts(21_181_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `3673` + // Minimum execution time: 19_707_000 picoseconds. + Weight::from_parts(20_048_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `363` + // Estimated: `3673` + // Minimum execution time: 17_953_000 picoseconds. + Weight::from_parts(18_378_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `396` + // Estimated: `3673` + // Minimum execution time: 17_270_000 picoseconds. + Weight::from_parts(17_685_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:1 w:0) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `345` + // Estimated: `3673` + // Minimum execution time: 17_366_000 picoseconds. + Weight::from_parts(17_762_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_author_inherent.rs b/container-chains/runtime-templates/simple/src/weights/pallet_author_inherent.rs new file mode 100644 index 0000000..f06850c --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_author_inherent.rs @@ -0,0 +1,74 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_author_inherent +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_author_inherent +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_author_inherent.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_author_inherent using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_author_inherent::WeightInfo for SubstrateWeight { + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorInherent::Author` (r:1 w:0) + /// Proof: `AuthorInherent::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `AuthoritiesNoting::Authorities` (r:1 w:0) + /// Proof: `AuthoritiesNoting::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorInherent::InherentIncluded` (r:0 w:1) + /// Proof: `AuthorInherent::InherentIncluded` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + fn kick_off_authorship_validation() -> Weight { + // Proof Size summary in bytes: + // Measured: `187` + // Estimated: `1672` + // Minimum execution time: 12_932_000 picoseconds. + Weight::from_parts(13_581_000, 1672) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_balances.rs b/container-chains/runtime-templates/simple/src/weights/pallet_balances.rs new file mode 100644 index 0000000..b6041ff --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_balances.rs @@ -0,0 +1,149 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_balances +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_balances +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_balances.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_balances using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_balances::WeightInfo for SubstrateWeight { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 63_803_000 picoseconds. + Weight::from_parts(64_609_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 50_479_000 picoseconds. + Weight::from_parts(51_005_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn force_set_balance_creating() -> Weight { + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 18_695_000 picoseconds. + Weight::from_parts(19_405_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn force_set_balance_killing() -> Weight { + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 25_827_000 picoseconds. + Weight::from_parts(26_550_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `103` + // Estimated: `6196` + // Minimum execution time: 65_924_000 picoseconds. + Weight::from_parts(66_718_000, 6196) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 62_763_000 picoseconds. + Weight::from_parts(63_554_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn force_unreserve() -> Weight { + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 23_162_000 picoseconds. + Weight::from_parts(23_471_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + u * (136 ±0)` + // Estimated: `990 + u * (2603 ±0)` + // Minimum execution time: 22_277_000 picoseconds. + Weight::from_parts(22_479_000, 990) + // Standard Error: 16_644 + .saturating_add(Weight::from_parts(17_839_906, 0).saturating_mul(u.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_cc_authorities_noting.rs b/container-chains/runtime-templates/simple/src/weights/pallet_cc_authorities_noting.rs new file mode 100644 index 0000000..f5599bf --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_cc_authorities_noting.rs @@ -0,0 +1,97 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_cc_authorities_noting +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_cc_authorities_noting +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_cc_authorities_noting.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_cc_authorities_noting using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_cc_authorities_noting::WeightInfo for SubstrateWeight { + /// Storage: `AuthoritiesNoting::DidSetOrchestratorAuthorityData` (r:1 w:1) + /// Proof: `AuthoritiesNoting::DidSetOrchestratorAuthorityData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthoritiesNoting::OrchestratorParaId` (r:1 w:0) + /// Proof: `AuthoritiesNoting::OrchestratorParaId` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthoritiesNoting::Authorities` (r:0 w:1) + /// Proof: `AuthoritiesNoting::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_latest_authorities_data() -> Weight { + // Proof Size summary in bytes: + // Measured: `141` + // Estimated: `1626` + // Minimum execution time: 28_903_000 picoseconds. + Weight::from_parts(29_809_000, 1626) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `AuthoritiesNoting::Authorities` (r:0 w:1) + /// Proof: `AuthoritiesNoting::Authorities` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 10]`. + fn set_authorities(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_084_000 picoseconds. + Weight::from_parts(7_663_864, 0) + // Standard Error: 1_880 + .saturating_add(Weight::from_parts(41_502, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AuthoritiesNoting::OrchestratorParaId` (r:0 w:1) + /// Proof: `AuthoritiesNoting::OrchestratorParaId` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_orchestrator_para_id() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_687_000 picoseconds. + Weight::from_parts(7_088_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_foreign_asset_creator.rs b/container-chains/runtime-templates/simple/src/weights/pallet_foreign_asset_creator.rs new file mode 100644 index 0000000..cc19907 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_foreign_asset_creator.rs @@ -0,0 +1,113 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_foreign_asset_creator +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_foreign_asset_creator +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_foreign_asset_creator.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_foreign_asset_creator using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_foreign_asset_creator::WeightInfo for SubstrateWeight { + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:1) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn create_foreign_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `112` + // Estimated: `3673` + // Minimum execution time: 24_643_000 picoseconds. + Weight::from_parts(25_270_000, 3673) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:2) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn change_existing_asset_type() -> Weight { + // Proof Size summary in bytes: + // Measured: `189` + // Estimated: `3654` + // Minimum execution time: 20_502_000 picoseconds. + Weight::from_parts(20_757_000, 3654) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:1) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn remove_existing_asset_type() -> Weight { + // Proof Size summary in bytes: + // Measured: `189` + // Estimated: `3654` + // Minimum execution time: 17_376_000 picoseconds. + Weight::from_parts(17_858_000, 3654) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ForeignAssetsCreator::AssetIdToForeignAsset` (r:1 w:1) + /// Proof: `ForeignAssetsCreator::AssetIdToForeignAsset` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:0 w:1) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn destroy_foreign_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `3893` + // Minimum execution time: 26_347_000 picoseconds. + Weight::from_parts(26_944_000, 3893) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_message_queue.rs b/container-chains/runtime-templates/simple/src/weights/pallet_message_queue.rs new file mode 100644 index 0000000..5ac5be9 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_message_queue.rs @@ -0,0 +1,186 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_message_queue +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_message_queue +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_message_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_message_queue using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_message_queue::WeightInfo for SubstrateWeight { + /// Storage: `MessageQueue::ServiceHead` (r:1 w:0) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn ready_ring_knit() -> Weight { + // Proof Size summary in bytes: + // Measured: `223` + // Estimated: `6044` + // Minimum execution time: 14_861_000 picoseconds. + Weight::from_parts(15_530_000, 6044) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + fn ready_ring_unknit() -> Weight { + // Proof Size summary in bytes: + // Measured: `218` + // Estimated: `6044` + // Minimum execution time: 13_333_000 picoseconds. + Weight::from_parts(13_775_000, 6044) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn service_queue_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `48` + // Estimated: `3517` + // Minimum execution time: 8_076_000 picoseconds. + Weight::from_parts(8_318_000, 3517) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn service_page_base_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `72` + // Estimated: `69050` + // Minimum execution time: 7_965_000 picoseconds. + Weight::from_parts(8_221_000, 69050) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn service_page_base_no_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `72` + // Estimated: `69050` + // Minimum execution time: 8_081_000 picoseconds. + Weight::from_parts(8_625_000, 69050) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:0 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn service_page_item() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 164_330_000 picoseconds. + Weight::from_parts(166_478_000, 0) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn bump_service_head() -> Weight { + // Proof Size summary in bytes: + // Measured: `171` + // Estimated: `3517` + // Minimum execution time: 8_338_000 picoseconds. + Weight::from_parts(8_728_000, 3517) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn reap_page() -> Weight { + // Proof Size summary in bytes: + // Measured: `65667` + // Estimated: `69050` + // Minimum execution time: 60_542_000 picoseconds. + Weight::from_parts(61_977_000, 69050) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn execute_overweight_page_removed() -> Weight { + // Proof Size summary in bytes: + // Measured: `65709` + // Estimated: `69050` + // Minimum execution time: 84_135_000 picoseconds. + Weight::from_parts(85_836_000, 69050) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) + fn execute_overweight_page_updated() -> Weight { + // Proof Size summary in bytes: + // Measured: `65709` + // Estimated: `69050` + // Minimum execution time: 121_491_000 picoseconds. + Weight::from_parts(122_480_000, 69050) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_multisig.rs b/container-chains/runtime-templates/simple/src/weights/pallet_multisig.rs new file mode 100644 index 0000000..0d25a06 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_multisig.rs @@ -0,0 +1,172 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_multisig +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_multisig +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_multisig.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_multisig using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_multisig::WeightInfo for SubstrateWeight { + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `z` is `[0, 10000]`. + fn as_multi_threshold_1(z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 24_349_000 picoseconds. + Weight::from_parts(25_677_854, 3997) + // Standard Error: 6 + .saturating_add(Weight::from_parts(562, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_create(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `296 + s * (2 ±0)` + // Estimated: `6811` + // Minimum execution time: 51_282_000 picoseconds. + Weight::from_parts(39_443_448, 6811) + // Standard Error: 695 + .saturating_add(Weight::from_parts(138_122, 0).saturating_mul(s.into())) + // Standard Error: 6 + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// The range of component `s` is `[3, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_approve(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `6811` + // Minimum execution time: 33_405_000 picoseconds. + Weight::from_parts(22_547_789, 6811) + // Standard Error: 540 + .saturating_add(Weight::from_parts(123_044, 0).saturating_mul(s.into())) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_409, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_complete(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `467 + s * (33 ±0)` + // Estimated: `6811 + s * (34 ±0)` + // Minimum execution time: 63_497_000 picoseconds. + Weight::from_parts(47_468_506, 6811) + // Standard Error: 1_327 + .saturating_add(Weight::from_parts(203_675, 0).saturating_mul(s.into())) + // Standard Error: 13 + .saturating_add(Weight::from_parts(1_709, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 34).saturating_mul(s.into())) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn approve_as_multi_create(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `296 + s * (2 ±0)` + // Estimated: `6811` + // Minimum execution time: 37_097_000 picoseconds. + Weight::from_parts(37_501_529, 6811) + // Standard Error: 852 + .saturating_add(Weight::from_parts(133_055, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn approve_as_multi_approve(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `6811` + // Minimum execution time: 20_380_000 picoseconds. + Weight::from_parts(20_476_099, 6811) + // Standard Error: 661 + .saturating_add(Weight::from_parts(114_276, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn cancel_as_multi(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `487 + s * (1 ±0)` + // Estimated: `6811` + // Minimum execution time: 38_289_000 picoseconds. + Weight::from_parts(38_641_314, 6811) + // Standard Error: 818 + .saturating_add(Weight::from_parts(127_222, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_proxy.rs b/container-chains/runtime-templates/simple/src/weights/pallet_proxy.rs new file mode 100644 index 0000000..c1d5139 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_proxy.rs @@ -0,0 +1,229 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_proxy +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_proxy +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_proxy.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_proxy using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_proxy::WeightInfo for SubstrateWeight { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `136 + p * (37 ±0)` + // Estimated: `4706 + p * (37 ±0)` + // Minimum execution time: 22_505_000 picoseconds. + Weight::from_parts(23_141_370, 4706) + // Standard Error: 1_367 + .saturating_add(Weight::from_parts(44_086, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `463 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698 + a * (68 ±0) + p * (37 ±0)` + // Minimum execution time: 48_501_000 picoseconds. + Weight::from_parts(49_157_317, 5698) + // Standard Error: 2_925 + .saturating_add(Weight::from_parts(199_580, 0).saturating_mul(a.into())) + // Standard Error: 3_022 + .saturating_add(Weight::from_parts(44_799, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 68).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `332 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 27_673_000 picoseconds. + Weight::from_parts(28_411_776, 5698) + // Standard Error: 1_558 + .saturating_add(Weight::from_parts(177_909, 0).saturating_mul(a.into())) + // Standard Error: 1_609 + .saturating_add(Weight::from_parts(9_160, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, _p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `332 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 27_127_000 picoseconds. + Weight::from_parts(28_442_417, 5698) + // Standard Error: 1_692 + .saturating_add(Weight::from_parts(176_430, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `349 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 36_732_000 picoseconds. + Weight::from_parts(36_783_203, 5698) + // Standard Error: 1_423 + .saturating_add(Weight::from_parts(171_673, 0).saturating_mul(a.into())) + // Standard Error: 1_471 + .saturating_add(Weight::from_parts(31_264, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `90 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 26_814_000 picoseconds. + Weight::from_parts(27_524_440, 4706) + // Standard Error: 1_112 + .saturating_add(Weight::from_parts(35_992, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `90 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 26_971_000 picoseconds. + Weight::from_parts(27_953_563, 4706) + // Standard Error: 2_104 + .saturating_add(Weight::from_parts(41_873, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `90 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 25_439_000 picoseconds. + Weight::from_parts(26_145_505, 4706) + // Standard Error: 1_112 + .saturating_add(Weight::from_parts(35_842, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `102` + // Estimated: `4706` + // Minimum execution time: 28_754_000 picoseconds. + Weight::from_parts(29_869_647, 4706) + // Standard Error: 1_097 + .saturating_add(Weight::from_parts(11_390, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 26_530_000 picoseconds. + Weight::from_parts(27_762_910, 4706) + // Standard Error: 1_122 + .saturating_add(Weight::from_parts(38_806, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_sudo.rs b/container-chains/runtime-templates/simple/src/weights/pallet_sudo.rs new file mode 100644 index 0000000..8740aa1 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_sudo.rs @@ -0,0 +1,99 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_sudo +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_sudo +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_sudo.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_sudo using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_sudo::WeightInfo for SubstrateWeight { + /// Storage: `Sudo::Key` (r:1 w:1) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn set_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `61` + // Estimated: `1517` + // Minimum execution time: 12_044_000 picoseconds. + Weight::from_parts(12_371_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn sudo() -> Weight { + // Proof Size summary in bytes: + // Measured: `61` + // Estimated: `1517` + // Minimum execution time: 13_453_000 picoseconds. + Weight::from_parts(13_823_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn sudo_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `61` + // Estimated: `1517` + // Minimum execution time: 13_584_000 picoseconds. + Weight::from_parts(13_913_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:1) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn remove_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `61` + // Estimated: `1517` + // Minimum execution time: 10_660_000 picoseconds. + Weight::from_parts(10_841_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_timestamp.rs b/container-chains/runtime-templates/simple/src/weights/pallet_timestamp.rs new file mode 100644 index 0000000..4889a5e --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_timestamp.rs @@ -0,0 +1,77 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_timestamp +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_timestamp +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_timestamp.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_timestamp using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_timestamp::WeightInfo for SubstrateWeight { + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `1493` + // Minimum execution time: 7_168_000 picoseconds. + Weight::from_parts(7_422_000, 1493) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_finalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `57` + // Estimated: `0` + // Minimum execution time: 4_355_000 picoseconds. + Weight::from_parts(4_506_000, 0) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_tx_pause.rs b/container-chains/runtime-templates/simple/src/weights/pallet_tx_pause.rs new file mode 100644 index 0000000..ca66c29 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_tx_pause.rs @@ -0,0 +1,79 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_tx_pause +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_tx_pause +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_tx_pause.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_tx_pause using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_tx_pause::WeightInfo for SubstrateWeight { + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn pause() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3997` + // Minimum execution time: 15_847_000 picoseconds. + Weight::from_parts(16_319_000, 3997) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn unpause() -> Weight { + // Proof Size summary in bytes: + // Measured: `566` + // Estimated: `3997` + // Minimum execution time: 21_731_000 picoseconds. + Weight::from_parts(21_996_000, 3997) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_utility.rs b/container-chains/runtime-templates/simple/src/weights/pallet_utility.rs new file mode 100644 index 0000000..51dfad4 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_utility.rs @@ -0,0 +1,121 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_utility +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_utility +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_utility.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_utility using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_utility::WeightInfo for SubstrateWeight { + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn batch(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 6_459_000 picoseconds. + Weight::from_parts(4_908_140, 3997) + // Standard Error: 4_662 + .saturating_add(Weight::from_parts(7_745_064, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn as_derivative() -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 13_558_000 picoseconds. + Weight::from_parts(13_869_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn batch_all(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 6_616_000 picoseconds. + Weight::from_parts(12_442_970, 3997) + // Standard Error: 4_575 + .saturating_add(Weight::from_parts(8_173_465, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + fn dispatch_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_709_000 picoseconds. + Weight::from_parts(10_116_000, 0) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 1000]`. + fn force_batch(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 6_387_000 picoseconds. + Weight::from_parts(11_551_976, 3997) + // Standard Error: 3_971 + .saturating_add(Weight::from_parts(7_665_569, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_xcm.rs b/container-chains/runtime-templates/simple/src/weights/pallet_xcm.rs new file mode 100644 index 0000000..6cea5df --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_xcm.rs @@ -0,0 +1,361 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_xcm.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_xcm::WeightInfo for SubstrateWeight { + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 29_508_000 picoseconds. + Weight::from_parts(29_927_000, 3540) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn teleport_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) + /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) + /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `371` + // Estimated: `607086` + // Minimum execution time: 162_318_000 picoseconds. + Weight::from_parts(166_336_000, 607086) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:1 w:0) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssetsCreator::ForeignAssetToAssetId` (r:1 w:0) + /// Proof: `ForeignAssetsCreator::ForeignAssetToAssetId` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(208), added: 2683, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `705` + // Estimated: `607086` + // Minimum execution time: 209_167_000 picoseconds. + Weight::from_parts(213_637_000, 607086) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn execute() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 13_494_000 picoseconds. + Weight::from_parts(13_806_000, 0) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_xcm_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 10_392_000 picoseconds. + Weight::from_parts(10_692_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn force_default_xcm_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_379_000 picoseconds. + Weight::from_parts(3_594_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_subscribe_version_notify() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 35_300_000 picoseconds. + Weight::from_parts(36_444_000, 3540) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_unsubscribe_version_notify() -> Weight { + // Proof Size summary in bytes: + // Measured: `257` + // Estimated: `3722` + // Minimum execution time: 36_800_000 picoseconds. + Weight::from_parts(37_255_000, 3722) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn force_suspension() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_352_000 picoseconds. + Weight::from_parts(3_666_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_supported_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `131` + // Estimated: `11021` + // Minimum execution time: 22_286_000 picoseconds. + Weight::from_parts(22_914_000, 11021) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_version_notifiers() -> Weight { + // Proof Size summary in bytes: + // Measured: `135` + // Estimated: `11025` + // Minimum execution time: 22_351_000 picoseconds. + Weight::from_parts(22_740_000, 11025) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn already_notified_target() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `13507` + // Minimum execution time: 23_089_000 picoseconds. + Weight::from_parts(23_499_000, 13507) + .saturating_add(T::DbWeight::get().reads(5_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_current_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `6082` + // Minimum execution time: 33_387_000 picoseconds. + Weight::from_parts(34_272_000, 6082) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn notify_target_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `172` + // Estimated: `8587` + // Minimum execution time: 12_399_000 picoseconds. + Weight::from_parts(12_775_000, 8587) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_version_notify_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `11032` + // Minimum execution time: 22_444_000 picoseconds. + Weight::from_parts(22_962_000, 11032) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn migrate_and_notify_old_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `148` + // Estimated: `11038` + // Minimum execution time: 42_340_000 picoseconds. + Weight::from_parts(43_044_000, 11038) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn new_query() -> Weight { + // Proof Size summary in bytes: + // Measured: `69` + // Estimated: `1554` + // Minimum execution time: 6_460_000 picoseconds. + Weight::from_parts(6_693_000, 1554) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `PolkadotXcm::Queries` (r:1 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn take_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `7706` + // Estimated: `11171` + // Minimum execution time: 34_877_000 picoseconds. + Weight::from_parts(35_303_000, 11171) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_xcm_benchmarks::generic.rs b/container-chains/runtime-templates/simple/src/weights/pallet_xcm_benchmarks::generic.rs new file mode 100644 index 0000000..6fd6a00 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_xcm_benchmarks::generic.rs @@ -0,0 +1,348 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm_benchmarks::generic +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm_benchmarks::generic +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_xcm_benchmarks::generic.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm_benchmarks::generic using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_xcm_benchmarks::generic::WeightInfo for SubstrateWeight { + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn report_holding() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 69_025_000 picoseconds. + Weight::from_parts(70_478_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + fn buy_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_108_000 picoseconds. + Weight::from_parts(3_187_000, 0) + } + /// Storage: `PolkadotXcm::Queries` (r:1 w:0) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn query_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `69` + // Estimated: `3534` + // Minimum execution time: 11_745_000 picoseconds. + Weight::from_parts(12_099_000, 3534) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn transact() -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 20_008_000 picoseconds. + Weight::from_parts(20_547_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + fn refund_surplus() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_290_000 picoseconds. + Weight::from_parts(3_378_000, 0) + } + fn set_error_handler() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_943_000 picoseconds. + Weight::from_parts(3_054_000, 0) + } + fn set_appendix() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_922_000 picoseconds. + Weight::from_parts(3_047_000, 0) + } + fn clear_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_994_000 picoseconds. + Weight::from_parts(3_093_000, 0) + } + fn descend_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_599_000 picoseconds. + Weight::from_parts(3_743_000, 0) + } + fn clear_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_933_000 picoseconds. + Weight::from_parts(3_009_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn report_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 59_555_000 picoseconds. + Weight::from_parts(61_159_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `126` + // Estimated: `3591` + // Minimum execution time: 18_218_000 picoseconds. + Weight::from_parts(18_471_000, 3591) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn trap() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_876_000 picoseconds. + Weight::from_parts(2_912_000, 0) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn subscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 30_762_000 picoseconds. + Weight::from_parts(31_259_000, 3540) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn unsubscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_046_000 picoseconds. + Weight::from_parts(6_273_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn burn_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_570_000 picoseconds. + Weight::from_parts(4_680_000, 0) + } + fn expect_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_133_000 picoseconds. + Weight::from_parts(3_207_000, 0) + } + fn expect_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_978_000 picoseconds. + Weight::from_parts(3_105_000, 0) + } + fn expect_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_881_000 picoseconds. + Weight::from_parts(2_996_000, 0) + } + fn expect_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_133_000 picoseconds. + Weight::from_parts(3_190_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn query_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 65_995_000 picoseconds. + Weight::from_parts(67_937_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + fn expect_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_558_000 picoseconds. + Weight::from_parts(7_866_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn report_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 59_627_000 picoseconds. + Weight::from_parts(61_599_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + fn clear_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_993_000 picoseconds. + Weight::from_parts(3_071_000, 0) + } + fn set_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_875_000 picoseconds. + Weight::from_parts(3_020_000, 0) + } + fn clear_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_898_000 picoseconds. + Weight::from_parts(2_985_000, 0) + } + fn set_fees_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_827_000 picoseconds. + Weight::from_parts(2_989_000, 0) + } + fn unpaid_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_980_000 picoseconds. + Weight::from_parts(3_035_000, 0) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/pallet_xcm_executor_utils.rs b/container-chains/runtime-templates/simple/src/weights/pallet_xcm_executor_utils.rs new file mode 100644 index 0000000..01f0695 --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/pallet_xcm_executor_utils.rs @@ -0,0 +1,99 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm_executor_utils +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm_executor_utils +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=benchmarking/frame-weight-runtime-template.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_xcm_executor_utils.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm_executor_utils using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_xcm_executor_utils::WeightInfo for SubstrateWeight { + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:0 w:1) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn set_reserve_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_492_000 picoseconds. + Weight::from_parts(9_941_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmExecutorUtils::ReservePolicy` (r:1 w:1) + /// Proof: `XcmExecutorUtils::ReservePolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn remove_reserve_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `87` + // Estimated: `607086` + // Minimum execution time: 13_785_000 picoseconds. + Weight::from_parts(14_331_000, 607086) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:0 w:1) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn set_teleport_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_586_000 picoseconds. + Weight::from_parts(9_794_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmExecutorUtils::TeleportPolicy` (r:1 w:1) + /// Proof: `XcmExecutorUtils::TeleportPolicy` (`max_values`: None, `max_size`: Some(603621), added: 606096, mode: `MaxEncodedLen`) + fn remove_teleport_policy() -> Weight { + // Proof Size summary in bytes: + // Measured: `87` + // Estimated: `607086` + // Minimum execution time: 13_974_000 picoseconds. + Weight::from_parts(14_248_000, 607086) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/weights/xcm/mod.rs b/container-chains/runtime-templates/simple/src/weights/xcm/mod.rs new file mode 100644 index 0000000..a42dc2c --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/xcm/mod.rs @@ -0,0 +1,251 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +pub mod pallet_xcm_benchmarks_generic; + +use { + crate::Runtime, + frame_support::weights::Weight, + pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric, + sp_std::prelude::*, + staging_xcm::{ + latest::{prelude::*, Weight as XCMWeight}, + DoubleEncoded, + }, +}; + +trait WeighMultiAssets { + fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; +} + +impl WeighMultiAssets for MultiAssets { + fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { + weight.saturating_mul(self.inner().iter().count() as u64) + } +} + +// Values copied from statemint benchmarks +const ASSET_BURN_MAX_PROOF_SIZE: u64 = 7242; +const ASSET_MINT_MAX_PROOF_SIZE: u64 = 7242; +const ASSET_TRANSFER_MAX_PROOF_SIZE: u64 = 13412; + +// For now we are returning benchmarked weights only for generic XCM instructions. +// Fungible XCM instructions will return a fixed weight value of +// 200_000_000 ref_time and its proper PoV weight taken from statemint benchmarks. +// +// TODO: add the fungible benchmarked values once these are calculated. +pub struct XcmWeight(core::marker::PhantomData); +impl XcmWeightInfo for XcmWeight +where + Runtime: frame_system::Config, +{ + fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts( + 200_000_000u64, + ASSET_BURN_MAX_PROOF_SIZE, + )) + } + fn reserve_asset_deposited(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts(200_000_000u64, 0)) + } + fn receive_teleported_asset(_assets: &MultiAssets) -> XCMWeight { + XCMWeight::MAX + } + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &Weight, + _querier: &Option, + ) -> XCMWeight { + XcmGeneric::::query_response() + } + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts( + 200_000_000u64, + ASSET_TRANSFER_MAX_PROOF_SIZE, + )) + } + fn transfer_reserve_asset( + assets: &MultiAssets, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + assets.weigh_multi_assets(XCMWeight::from_parts( + 200_000_000u64, + ASSET_TRANSFER_MAX_PROOF_SIZE, + )) + } + fn transact( + _origin_type: &OriginKind, + _require_weight_at_most: &Weight, + _call: &DoubleEncoded, + ) -> XCMWeight { + XcmGeneric::::transact() + } + fn hrmp_new_channel_open_request( + _sender: &u32, + _max_message_size: &u32, + _max_capacity: &u32, + ) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX + } + fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX + } + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX + } + fn clear_origin() -> XCMWeight { + XcmGeneric::::clear_origin() + } + fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { + XcmGeneric::::descend_origin() + } + fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::report_error() + } + fn deposit_asset(_assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { + Weight::from_parts(200_000_000u64, ASSET_MINT_MAX_PROOF_SIZE) + } + fn deposit_reserve_asset( + _assets: &MultiAssetFilter, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + Weight::from_parts(200_000_000u64, ASSET_MINT_MAX_PROOF_SIZE) + } + fn exchange_asset( + _give: &MultiAssetFilter, + _receive: &MultiAssets, + _maximal: &bool, + ) -> XCMWeight { + Weight::MAX + } + fn initiate_reserve_withdraw( + _assets: &MultiAssetFilter, + _reserve: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + XCMWeight::from_parts(200_000_000u64, ASSET_TRANSFER_MAX_PROOF_SIZE) + } + fn initiate_teleport( + _assets: &MultiAssetFilter, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + XCMWeight::MAX + } + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight { + XcmGeneric::::report_holding() + } + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { + XcmGeneric::::buy_execution() + } + fn refund_surplus() -> XCMWeight { + XcmGeneric::::refund_surplus() + } + fn set_error_handler(_xcm: &Xcm) -> XCMWeight { + XcmGeneric::::set_error_handler() + } + fn set_appendix(_xcm: &Xcm) -> XCMWeight { + XcmGeneric::::set_appendix() + } + fn clear_error() -> XCMWeight { + XcmGeneric::::clear_error() + } + fn claim_asset(assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::claim_asset()) + } + fn trap(_code: &u64) -> XCMWeight { + XcmGeneric::::trap() + } + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> XCMWeight { + XcmGeneric::::subscribe_version() + } + fn unsubscribe_version() -> XCMWeight { + XcmGeneric::::unsubscribe_version() + } + fn burn_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> Weight { + XcmGeneric::::expect_origin() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight { + XcmGeneric::::expect_error() + } + fn expect_transact_status(_transact_status: &MaybeErrorCode) -> Weight { + XcmGeneric::::expect_transact_status() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::query_pallet() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> Weight { + XcmGeneric::::expect_pallet() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_transact_status() + } + fn clear_transact_status() -> Weight { + XcmGeneric::::clear_transact_status() + } + fn universal_origin(_: &Junction) -> Weight { + Weight::MAX + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + Weight::MAX + } + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX + } + fn set_fees_mode(_: &bool) -> Weight { + XcmGeneric::::set_fees_mode() + } + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() + } + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() + } + fn alias_origin(_: &MultiLocation) -> Weight { + // XCM Executor does not currently support alias origin operations + Weight::MAX + } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { + XcmGeneric::::unpaid_execution() + } +} diff --git a/container-chains/runtime-templates/simple/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/container-chains/runtime-templates/simple/src/weights/xcm/pallet_xcm_benchmarks_generic.rs new file mode 100644 index 0000000..d5ac11e --- /dev/null +++ b/container-chains/runtime-templates/simple/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -0,0 +1,348 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_xcm_benchmarks::generic +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/container-chain-simple-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_xcm_benchmarks::generic +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-runtime-template-xcm.hbs +// --json-file +// raw.json +// --output +// tmp/simple_template_weights/pallet_xcm_benchmarks::generic.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for pallet_xcm_benchmarks::generic using the Substrate node and recommended hardware. +pub struct WeightInfo(PhantomData); +impl WeightInfo { + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn report_holding() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 67_455_000 picoseconds. + Weight::from_parts(68_733_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + pub(crate) fn buy_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_084_000 picoseconds. + Weight::from_parts(3_180_000, 0) + } + /// Storage: `PolkadotXcm::Queries` (r:1 w:0) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn query_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `69` + // Estimated: `3534` + // Minimum execution time: 11_782_000 picoseconds. + Weight::from_parts(12_021_000, 3534) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + pub(crate) fn transact() -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3997` + // Minimum execution time: 19_731_000 picoseconds. + Weight::from_parts(20_028_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + pub(crate) fn refund_surplus() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_280_000 picoseconds. + Weight::from_parts(3_377_000, 0) + } + pub(crate) fn set_error_handler() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_056_000 picoseconds. + Weight::from_parts(3_155_000, 0) + } + pub(crate) fn set_appendix() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_974_000 picoseconds. + Weight::from_parts(3_054_000, 0) + } + pub(crate) fn clear_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_915_000 picoseconds. + Weight::from_parts(3_032_000, 0) + } + pub(crate) fn descend_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_671_000 picoseconds. + Weight::from_parts(3_779_000, 0) + } + pub(crate) fn clear_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_894_000 picoseconds. + Weight::from_parts(2_989_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn report_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 59_230_000 picoseconds. + Weight::from_parts(60_371_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn claim_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `126` + // Estimated: `3591` + // Minimum execution time: 17_200_000 picoseconds. + Weight::from_parts(17_745_000, 3591) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + pub(crate) fn trap() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_880_000 picoseconds. + Weight::from_parts(3_020_000, 0) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn subscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `75` + // Estimated: `3540` + // Minimum execution time: 29_747_000 picoseconds. + Weight::from_parts(30_337_000, 3540) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn unsubscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_659_000 picoseconds. + Weight::from_parts(5_843_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + pub(crate) fn burn_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_334_000 picoseconds. + Weight::from_parts(4_461_000, 0) + } + pub(crate) fn expect_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_118_000 picoseconds. + Weight::from_parts(3_203_000, 0) + } + pub(crate) fn expect_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_983_000 picoseconds. + Weight::from_parts(3_125_000, 0) + } + pub(crate) fn expect_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_969_000 picoseconds. + Weight::from_parts(3_033_000, 0) + } + pub(crate) fn expect_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_231_000 picoseconds. + Weight::from_parts(3_355_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn query_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 66_266_000 picoseconds. + Weight::from_parts(66_995_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + pub(crate) fn expect_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_645_000 picoseconds. + Weight::from_parts(7_851_000, 0) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub(crate) fn report_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3641` + // Minimum execution time: 59_386_000 picoseconds. + Weight::from_parts(60_484_000, 3641) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_986_000 picoseconds. + Weight::from_parts(3_089_000, 0) + } + pub(crate) fn set_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_940_000 picoseconds. + Weight::from_parts(3_064_000, 0) + } + pub(crate) fn clear_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_920_000 picoseconds. + Weight::from_parts(3_006_000, 0) + } + pub(crate) fn set_fees_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_926_000 picoseconds. + Weight::from_parts(3_021_000, 0) + } + pub(crate) fn unpaid_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_969_000 picoseconds. + Weight::from_parts(3_114_000, 0) + } +} \ No newline at end of file diff --git a/container-chains/runtime-templates/simple/src/xcm_config.rs b/container-chains/runtime-templates/simple/src/xcm_config.rs new file mode 100644 index 0000000..909801e --- /dev/null +++ b/container-chains/runtime-templates/simple/src/xcm_config.rs @@ -0,0 +1,425 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + super::{ + currency::MICROUNIT, weights, weights::xcm::XcmWeight as XcmGenericWeights, AccountId, + AllPalletsWithSystem, AssetRate, Balance, Balances, ForeignAssetsCreator, MaintenanceMode, + MessageQueue, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeBlockWeights, + RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, + }, + cumulus_primitives_core::{AggregateMessageOrigin, ParaId}, + frame_support::{ + parameter_types, + traits::{Everything, Nothing, PalletInfoAccess, TransformOrigin}, + weights::Weight, + }, + frame_system::EnsureRoot, + pallet_xcm::XcmPassthrough, + pallet_xcm_executor_utils::{ + filters::{IsReserveFilter, IsTeleportFilter}, + DefaultTrustPolicy, + }, + parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}, + polkadot_runtime_common::xcm_sender::ExponentialPrice, + sp_core::ConstU32, + sp_runtime::Perbill, + staging_xcm::latest::prelude::*, + staging_xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FungibleAdapter, + IsConcrete, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + WithComputedOrigin, + }, + staging_xcm_executor::XcmExecutor, +}; + +parameter_types! { + // Self Reserve location, defines the multilocation identifying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a MultiLocation: (Self Balances pallet index) + // We use the RELATIVE multilocation + pub SelfReserve: MultiLocation = MultiLocation { + parents:0, + interior: Junctions::X1( + PalletInstance(::index() as u8) + ) + }; + + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); + + // TODO: revisit + pub const RelayNetwork: NetworkId = NetworkId::Westend; + + // The relay chain Origin type + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + + pub const MaxAssetsIntoHolding: u32 = 64; + + /// Maximum number of instructions in a single XCM fragment. A sanity check against + /// weight caculations getting too crazy. + pub MaxInstructions: u32 = 100; + + // The universal location within the global consensus system + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); + + pub const BaseDeliveryFee: u128 = 100 * MICROUNIT; +} + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // If we receive a MultiLocation of type AccountKey20, just generate a native account + AccountId32Aliases, + // Generate remote accounts according to polkadot standards + staging_xcm_builder::HashedDescription< + AccountId, + staging_xcm_builder::DescribeFamily, + >, +); + +/// Local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +/// Means for transacting the native currency on this chain. +pub type CurrencyTransactor = FungibleAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Convert an XCM MultiLocation into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports of `Balances`. + (), +>; + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `RuntimeOrigin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +/// Means for transacting assets on this chain. +pub type AssetTransactors = (CurrencyTransactor, ForeignFungiblesTransactor); +pub type XcmWeigher = + WeightInfoBounds, RuntimeCall, MaxInstructions>; +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +pub struct XcmConfig; +impl staging_xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = IsReserveFilter; + type IsTeleporter = IsTeleportFilter; + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = XcmWeigher; + type Trader = ( + UsingComponents, + cumulus_primitives_utility::TakeFirstAssetTrader< + AccountId, + AssetRateAsMultiplier, + // Use this currency when it is a fungible asset matching the given location or name: + (ConvertedConcreteId,), + ForeignAssets, + (), + >, + ); + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // TODO pallet-xcm weights + type WeightInfo = weights::pallet_xcm::SubstrateWeight; + type AdminOrigin = EnsureRoot; +} + +pub type PriceForSiblingParachainDelivery = + ExponentialPrice; + +pub type PriceForParentDelivery = + ExponentialPrice; + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = weights::cumulus_pallet_xcmp_queue::SubstrateWeight; + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; + // Enqueue XCMP messages from siblings for later processing. + type XcmpQueue = TransformOrigin; + type MaxInboundSuspended = sp_core::ConstU32<1_000>; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +parameter_types! { + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type WeightInfo = weights::cumulus_pallet_dmp_queue::SubstrateWeight; + type RuntimeEvent = RuntimeEvent; + type DmpSink = frame_support::traits::EnqueueWithOrigin; +} + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_message_queue::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = staging_xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + XcmExecutor, + RuntimeCall, + >; + type Size = u32; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + // NarrowOriginToSibling calls XcmpQueue's is_pause if Origin is sibling. Allows all other origins + type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + // TODO verify values + type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; + type MaxStale = sp_core::ConstU32<8>; + type ServiceWeight = MessageQueueServiceWeight; +} + +parameter_types! { + // we just reuse the same deposits + pub const ForeignAssetsAssetDeposit: Balance = 0; + pub const ForeignAssetsAssetAccountDeposit: Balance = 0; + pub const ForeignAssetsApprovalDeposit: Balance = 0; + pub const ForeignAssetsAssetsStringLimit: u32 = 50; + pub const ForeignAssetsMetadataDepositBase: Balance = 0; + pub const ForeignAssetsMetadataDepositPerByte: Balance = 0; + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +#[cfg(feature = "runtime-benchmarks")] +/// Simple conversion of `u32` into an `AssetId` for use in benchmarking. +pub struct ForeignAssetBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_assets::BenchmarkHelper for ForeignAssetBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> AssetId { + id.try_into() + .expect("number too large to create benchmarks") + } +} +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_rate::AssetKindFactory for ForeignAssetBenchmarkHelper { + fn create_asset_kind(id: u32) -> AssetId { + id.try_into() + .expect("number too large to create benchmarks") + } +} + +pub type AssetId = u16; +pub type ForeignAssetsInstance = pallet_assets::Instance1; +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetIdParameter = AssetId; + type Currency = Balances; + type CreateOrigin = frame_support::traits::NeverEnsureOrigin; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ForeignAssetsAssetDeposit; + type MetadataDepositBase = ForeignAssetsMetadataDepositBase; + type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; + type ApprovalDeposit = ForeignAssetsApprovalDeposit; + type StringLimit = ForeignAssetsAssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = weights::pallet_assets::SubstrateWeight; + type CallbackHandle = (); + type AssetAccountDeposit = ForeignAssetsAssetAccountDeposit; + type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ForeignAssetBenchmarkHelper; +} + +impl pallet_foreign_asset_creator::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForeignAsset = MultiLocation; + type ForeignAssetCreatorOrigin = EnsureRoot; + type ForeignAssetModifierOrigin = EnsureRoot; + type ForeignAssetDestroyerOrigin = EnsureRoot; + type Fungibles = ForeignAssets; + type WeightInfo = weights::pallet_foreign_asset_creator::SubstrateWeight; + type OnForeignAssetCreated = (); + type OnForeignAssetDestroyed = (); +} + +impl pallet_asset_rate::Config for Runtime { + type CreateOrigin = EnsureRoot; + type RemoveOrigin = EnsureRoot; + type UpdateOrigin = EnsureRoot; + type Currency = Balances; + type AssetKind = AssetId; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_asset_rate::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ForeignAssetBenchmarkHelper; +} + +use { + crate::ForeignAssets, + staging_xcm_builder::{FungiblesAdapter, NoChecking}, + staging_xcm_executor::traits::JustTry, +}; + +/// Means for transacting foreign assets from different global consensus. +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + ForeignAssets, + // Use this currency when it is a fungible asset matching the given location or name: + (ConvertedConcreteId,), + // Convert an XCM MultiLocation into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont need to check teleports here. + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; + +/// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. +pub type AssetRateAsMultiplier = + parachains_common::xcm_config::AssetFeeAsExistentialDepositMultiplier< + Runtime, + WeightToFee, + AssetRate, + ForeignAssetsInstance, + >; + +parameter_types! { + pub const TrustPolicyMaxAssets: u32 = 1000; + pub const AllNativeTrustPolicy: DefaultTrustPolicy = DefaultTrustPolicy::AllNative; + pub const AllNeverTrustPolicy: DefaultTrustPolicy = DefaultTrustPolicy::Never; +} +impl pallet_xcm_executor_utils::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type TrustPolicyMaxAssets = TrustPolicyMaxAssets; + type ReserveDefaultTrustPolicy = AllNativeTrustPolicy; + type SetReserveTrustOrigin = EnsureRoot; + type TeleportDefaultTrustPolicy = AllNeverTrustPolicy; + type SetTeleportTrustOrigin = EnsureRoot; + type WeightInfo = weights::pallet_xcm_executor_utils::SubstrateWeight; +} diff --git a/custom-pallets/department-funding/Cargo.toml b/custom-pallets/department-funding/Cargo.toml new file mode 100644 index 0000000..f9be831 --- /dev/null +++ b/custom-pallets/department-funding/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "pallet-department-funding" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true , optional = true} +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +pallet-timestamp = { workspace = true } +pallet-balances = { workspace = true } +pallet-support = { workspace = true } +pallet-shared-storage = { workspace = true } +trait-shared-storage = { workspace = true } +pallet-schelling-game-shared = { workspace = true } +trait-schelling-game-shared = { workspace = true } +pallet-sortition-sum-game = { workspace = true } + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +frame-support-test = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "pallet-timestamp/std", + "pallet-balances/std", + "pallet-support/std", + "pallet-shared-storage/std", + "pallet-schelling-game-shared/std", + "pallet-sortition-sum-game/std", + "frame-support-test/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/department-funding/README.md b/custom-pallets/department-funding/README.md similarity index 100% rename from pallets/department-funding/README.md rename to custom-pallets/department-funding/README.md diff --git a/custom-pallets/department-funding/department-funding-rpc/Cargo.toml b/custom-pallets/department-funding/department-funding-rpc/Cargo.toml new file mode 100644 index 0000000..5310aa2 --- /dev/null +++ b/custom-pallets/department-funding/department-funding-rpc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "department-funding-rpc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +jsonrpsee = { workspace = true } +sc-rpc = { workspace = true} +sp-api = { workspace = true } +sp-blockchain = {workspace = true } +sp-runtime = { workspace = true } +department-funding-runtime-api = { workspace = true} \ No newline at end of file diff --git a/pallets/department-funding/department-funding-rpc/src/lib.rs b/custom-pallets/department-funding/department-funding-rpc/src/lib.rs similarity index 100% rename from pallets/department-funding/department-funding-rpc/src/lib.rs rename to custom-pallets/department-funding/department-funding-rpc/src/lib.rs diff --git a/custom-pallets/department-funding/department-funding-runtime-api/Cargo.toml b/custom-pallets/department-funding/department-funding-runtime-api/Cargo.toml new file mode 100644 index 0000000..2dfaa5a --- /dev/null +++ b/custom-pallets/department-funding/department-funding-runtime-api/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "department-funding-runtime-api" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +parity-scale-codec = { workspace = true } +sp-api = { workspace = true } +frame-support = { workspace = true } +sp-std = { workspace = true} + +[features] +default = ["std"] +std = [ + "sp-api/std", + "frame-support/std", + "sp-std/std", +] \ No newline at end of file diff --git a/custom-pallets/department-funding/department-funding-runtime-api/src/lib.rs b/custom-pallets/department-funding/department-funding-runtime-api/src/lib.rs new file mode 100644 index 0000000..6ca1347 --- /dev/null +++ b/custom-pallets/department-funding/department-funding-runtime-api/src/lib.rs @@ -0,0 +1,19 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// use frame_support::sp_std::{vec::Vec}; +// or +use parity_scale_codec::Codec; +use sp_std::prelude::*; + +type DepartmentRequiredFundId = u64; + +sp_api::decl_runtime_apis! { + pub trait DepartmentFundingApi where AccountId: Codec { + fn get_evidence_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; + fn get_staking_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; + fn get_drawing_period_end(department_required_fund_id: DepartmentRequiredFundId) -> (u64, u64, bool); + fn get_commit_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; + fn get_vote_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; + fn selected_as_juror(department_required_fund_id: DepartmentRequiredFundId, who: AccountId) -> bool; + } +} diff --git a/custom-pallets/department-funding/src/benchmarking.rs b/custom-pallets/department-funding/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/department-funding/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/department-funding/src/extras.rs b/custom-pallets/department-funding/src/extras.rs new file mode 100644 index 0000000..f478976 --- /dev/null +++ b/custom-pallets/department-funding/src/extras.rs @@ -0,0 +1,287 @@ +use crate::*; +use types::{TIME_FOR_STAKING_FUNDING_STATUS_FAILED, TIME_FOR_STAKING_FUNDING_STATUS_PASSED}; + +impl DepartmentRequiredFund { + pub fn new( + department_required_fund_id: DepartmentRequiredFundId, + department_id: DepartmentId, + content: Content, + tipping_name: TippingName, + funding_needed: BalanceOf, + creator: T::AccountId, + ) -> Self { + DepartmentRequiredFund { + created: new_who_and_when::(creator.clone()), + department_required_fund_id, + content, + department_id, + tipping_name, + funding_needed, + creator, + } + } +} + +impl Pallet { + pub(super) fn get_phase_data() -> PhaseData { + T::SchellingGameSharedSource::create_phase_data(50, 5, 3, 100, (100, 100)) + } + + pub fn ensure_validation_to_do( + department_required_fund_id: DepartmentRequiredFundId, + ) -> DispatchResult { + let bool_data = ValidateDepartmentRequiredFund::::get(department_required_fund_id); + ensure!( + bool_data == true, + Error::::ValidationForDepartmentRequiredFundIdIsOff + ); + + Ok(()) + } + + pub fn get_department_id_from_department_required_fund_id( + department_required_fund_id: DepartmentRequiredFundId, + ) -> Result { + let department_required_fund_option = + DepartmentRequiredFunds::::get(department_required_fund_id); + + match department_required_fund_option { + Some(department_required_fund) => Ok(department_required_fund.department_id), + None => Err(Error::::DepartmentRequiredFundDontExits)?, + } + } + + pub fn ensure_can_stake_using_status( + department_id: DepartmentId, + ) -> Result, FundingStatus>, DispatchError> { + let now = >::block_number(); + let department_funding_status = DepartmentFundingStatus { + block_number: now, + status: FundingStatus::Processing, + }; + let department_status_option = + DepartmentFundingStatusForDepartmentId::::get(department_id); + match department_status_option { + Some(department_status) => { + let funding_status = department_status.status; + if funding_status == FundingStatus::Processing { + Err(Error::::FundingStatusProcessing.into()) + } else if funding_status == FundingStatus::Failed { + // else check 3 month if status faild or 6 months if status success to reapply for funding + let status_failed_time = TIME_FOR_STAKING_FUNDING_STATUS_FAILED; + let status_failed_time_block = Self::u64_to_block_saturated(status_failed_time); + let funding_status_block = department_status.block_number; + let time = now.checked_sub(&funding_status_block).expect("Overflow"); + if time >= status_failed_time_block { + Ok(department_funding_status) + } else { + Err(Error::::ReapplicationTimeNotReached.into()) + } + } else if funding_status == FundingStatus::Success { + let status_success_time = TIME_FOR_STAKING_FUNDING_STATUS_PASSED; + let status_success_time_block = + Self::u64_to_block_saturated(status_success_time); + let funding_status_block = department_status.block_number; + let time = now.checked_sub(&funding_status_block).expect("Overflow"); + if time >= status_success_time_block { + Ok(department_funding_status) + } else { + Err(Error::::ReapplicationTimeNotReached.into()) + } + } else { + Err(Error::::ConditionDontMatch.into()) + } + } + None => Ok(department_funding_status), + } + } + + // pub fn ensure_user_is_project_creator_and_project_exists( + // project_id: ProjectId, + // user: T::AccountId, + // ) -> DispatchResult { + // let project_option: Option> = Projects::get(project_id); + // match project_option { + // Some(project) => { + // let creator = project.creator; + // ensure!(creator == user, Error::::ProjectCreatorDontMatch); + // }, + // None => Err(Error::::ProjectDontExists)?, + // } + + // Ok(()) + // } + + // pub fn ensure_staking_period_set_once_project_id(project_id: ProjectId) -> DispatchResult { + // let block_number_option = >::get(project_id); + // match block_number_option { + // Some(_block) => Err(Error::::ProjectIdStakingPeriodAlreadySet)?, + // None => Ok(()), + // } + // } + + pub fn get_block_number_of_schelling_game( + department_required_fund_id: DepartmentRequiredFundId, + ) -> Result, DispatchError> { + let block_number_option = >::get(department_required_fund_id); + let block_number = match block_number_option { + Some(block_number) => block_number, + None => Err(Error::::BlockDepartmentRequiredFundIdNotExists)?, + }; + Ok(block_number) + } + + pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { + input.saturated_into::>() + } + + pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { + input.saturated_into::>() + } + + pub(super) fn value_of_tipping_name(tipping: TippingName) -> TippingValue> { + match tipping { + TippingName::SmallTipper => TippingValue { + max_tipping_value: 10_000u64.saturated_into::>(), + stake_required: 10u64.saturated_into::>(), + }, + TippingName::BigTipper => TippingValue { + max_tipping_value: 100_000u64.saturated_into::>(), + stake_required: 50u64.saturated_into::>(), + }, + TippingName::SmallSpender => TippingValue { + max_tipping_value: 1_000_000u64.saturated_into::>(), + stake_required: 100u64.saturated_into::>(), + }, + TippingName::MediumSpender => TippingValue { + max_tipping_value: 10_000_000u64.saturated_into::>(), + stake_required: 200u64.saturated_into::>(), + }, + TippingName::BigSpender => TippingValue { + max_tipping_value: 100_000_000u64.saturated_into::>(), + stake_required: 500u64.saturated_into::>(), + }, + } + } + + pub fn get_evidence_period_end_block( + department_required_fund_id: DepartmentRequiredFundId, + ) -> Option { + let now = >::block_number(); + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + // End block code start + + pub fn get_staking_period_end_block( + department_required_fund_id: DepartmentRequiredFundId, + ) -> Option { + let now = >::block_number(); + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_drawing_period_end( + department_required_fund_id: DepartmentRequiredFundId, + ) -> (u64, u64, bool) { + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + let phase_data = Self::get_phase_data(); + + let result = + T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); + result + } + + pub fn get_commit_period_end_block( + department_required_fund_id: DepartmentRequiredFundId, + ) -> Option { + let now = >::block_number(); + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_vote_period_end_block( + department_required_fund_id: DepartmentRequiredFundId, + ) -> Option { + let now = >::block_number(); + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn selected_as_juror( + department_required_fund_id: DepartmentRequiredFundId, + who: T::AccountId, + ) -> bool { + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); + result + } + + // End block code end +} diff --git a/custom-pallets/department-funding/src/lib.rs b/custom-pallets/department-funding/src/lib.rs new file mode 100644 index 0000000..6cbc6c8 --- /dev/null +++ b/custom-pallets/department-funding/src/lib.rs @@ -0,0 +1,448 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +// One can enhance validation measures by increasing staking power for local residents or individuals with positive externalities—those who contribute to the network for a good cause. +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +mod extras; +mod types; + +use frame_support::pallet_prelude::DispatchError; +use frame_support::pallet_prelude::*; +use frame_support::sp_runtime::traits::{CheckedAdd, CheckedSub}; +use frame_support::sp_runtime::SaturatedConversion; +use frame_support::{dispatch::DispatchResult, ensure}; +use frame_system::pallet_prelude::*; +use sp_std::prelude::*; + +use frame_support::{ + traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, + PalletId, +}; +use pallet_schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType}; +use pallet_sortition_sum_game::types::SumTreeName; +use pallet_support::{ + ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, PostId, WhoAndWhen, + WhoAndWhenOf, +}; +use trait_schelling_game_shared::SchellingGameSharedLink; +use trait_shared_storage::SharedStorageLink; +pub use types::DEPARTMENT_REQUIRED_FUND_ID; +use types::{ + DepartmentFundingStatus, DepartmentRequiredFund, FundingStatus, TippingName, TippingValue, +}; + +type AccountIdOf = ::AccountId; +type BalanceOf = <::Currency as Currency>>::Balance; +pub type BlockNumberOf = BlockNumberFor; +pub type SumTreeNameType = SumTreeName, BlockNumberOf>; +type DepartmentId = u64; +type DepartmentRequiredFundId = u64; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_schelling_game_shared::Config + pallet_timestamp::Config + { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + + type SharedStorageSource: SharedStorageLink>; + type SchellingGameSharedSource: SchellingGameSharedLink< + SumTreeName = SumTreeName>, + SchellingGameType = SchellingGameType, + BlockNumber = BlockNumberOf, + AccountId = AccountIdOf, + Balance = BalanceOf, + RangePoint = RangePoint, + Period = Period, + PhaseData = PhaseData, + >; + type Currency: ReservableCurrency; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + #[pallet::type_value] + pub fn MinimumDepartmentStake() -> BalanceOf { + 10000u128.saturated_into::>() + } + + #[pallet::type_value] + pub fn DefaultForNextDepartmentRequiredFundId() -> DepartmentRequiredFundId { + DEPARTMENT_REQUIRED_FUND_ID + } + + #[pallet::storage] + #[pallet::getter(fn next_department_required_fund_id)] + pub type NextDepartmentRequiredFundId = StorageValue< + _, + DepartmentRequiredFundId, + ValueQuery, + DefaultForNextDepartmentRequiredFundId, + >; + + #[pallet::storage] + #[pallet::getter(fn get_department_required_funds)] + pub type DepartmentRequiredFunds = + StorageMap<_, Blake2_128Concat, DepartmentRequiredFundId, DepartmentRequiredFund>; + + #[pallet::storage] + #[pallet::getter(fn validate_positive_externality)] + pub type ValidateDepartmentRequiredFund = + StorageMap<_, Twox64Concat, DepartmentRequiredFundId, bool, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn validation_block)] + pub type ValidationBlock = + StorageMap<_, Blake2_128Concat, DepartmentRequiredFundId, BlockNumberOf>; + + #[pallet::storage] + #[pallet::getter(fn department_funding_status)] + pub type DepartmentFundingStatusForDepartmentId = StorageMap< + _, + Blake2_128Concat, + DepartmentId, + DepartmentFundingStatus, FundingStatus>, + >; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + DepartmentFundCreated { + account: T::AccountId, + department_required_fund_id: DepartmentRequiredFundId, + }, + StakingPeriodStarted { + department_required_fund_id: DepartmentRequiredFundId, + block_number: BlockNumberOf, + }, + ApplyJurors { + department_required_fund_id: DepartmentRequiredFundId, + block_number: BlockNumberOf, + account: T::AccountId, + }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + LessThanMinStake, + CannotStakeNow, + ChoiceOutOfRange, + FundingMoreThanTippingValue, + DepartmentRequiredFundDontExits, + BlockDepartmentRequiredFundIdNotExists, + ValidationForDepartmentRequiredFundIdIsOff, + FundingStatusProcessing, + ReapplicationTimeNotReached, + ConditionDontMatch, + } + + // Check deparment exists, it will done using loose coupling + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn create_department_required_fund( + origin: OriginFor, + department_id: DepartmentId, + content: Content, + tipping_name: TippingName, + funding_needed: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let tipping_value = Self::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let stake_required = tipping_value.stake_required; + let new_department_fund_id = Self::next_department_required_fund_id(); + let new_department_fund: DepartmentRequiredFund = DepartmentRequiredFund::new( + new_department_fund_id, + department_id, + content, + tipping_name, + funding_needed, + who.clone(), + ); + ensure!( + funding_needed <= max_tipping_value, + Error::::FundingMoreThanTippingValue + ); + // Check user has done kyc + let _ = ::Currency::withdraw( + &who, + stake_required, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; + DepartmentRequiredFunds::insert(new_department_fund_id, new_department_fund); + NextDepartmentRequiredFundId::::mutate(|n| { + *n += 1; + }); + + Self::deposit_event(Event::DepartmentFundCreated { + account: who, + department_required_fund_id: new_department_fund_id, + }); + Ok(()) + } + + // Check update and discussion time over, only project creator can apply staking period + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn apply_staking_period( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::ensure_validation_to_do(department_required_fund_id)?; + let department_id = Self::get_department_id_from_department_required_fund_id( + department_required_fund_id, + )?; + let department_funding_status = Self::ensure_can_stake_using_status(department_id)?; + DepartmentFundingStatusForDepartmentId::::insert( + department_id, + department_funding_status, + ); + let now = >::block_number(); + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: now.clone(), + }; + ValidationBlock::::insert(department_required_fund_id, now.clone()); + T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now.clone())?; + T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; + + Self::deposit_event(Event::StakingPeriodStarted { + department_required_fund_id, + block_number: now, + }); + + Ok(()) + } + + // // Check update and discussion time over, only project creator can apply staking period + // #[pallet::call_index(1)] + // #[pallet::weight(0)] + // pub fn apply_staking_period(origin: OriginFor, department_required_fund_id: DepartmentRequiredFundId) -> DispatchResult { + // let who = ensure_signed(origin)?; + + // Self::ensure_user_is_project_creator_and_project_exists(project_id, who)?; + // Self::ensure_staking_period_set_once_project_id(project_id)?; + + // let now = >::block_number(); + + // let key = SumTreeName::ProjectTips { project_id, block_number: now.clone() }; + + // >::insert(project_id, now.clone()); + // // check what if called again, its done with `ensure_staking_period_set_once_project_id` + // T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now.clone())?; + // T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; + + // Self::deposit_event(Event::StakinPeriodStarted { project_id, block_number: now }); + + // Ok(()) + // } + + #[pallet::call_index(2)] + #[pallet::weight(0)] + pub fn apply_jurors( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + stake: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id)?; + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::apply_jurors_helper_link( + key, + phase_data, + who.clone(), + stake, + )?; + Self::deposit_event(Event::ApplyJurors { + department_required_fund_id, + block_number, + account: who, + }); + + Ok(()) + } + + #[pallet::call_index(3)] + #[pallet::weight(0)] + pub fn pass_period( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id)?; + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let now = >::block_number(); + let phase_data = Self::get_phase_data(); + T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; + Ok(()) + } + + #[pallet::call_index(4)] + #[pallet::weight(0)] + pub fn draw_jurors( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + iterations: u64, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id)?; + + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; + + Ok(()) + } + + // Unstaking + // Stop drawn juror to unstake ✔️ + #[pallet::call_index(5)] + #[pallet::weight(0)] + pub fn unstaking( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id)?; + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; + Ok(()) + } + + #[pallet::call_index(6)] + #[pallet::weight(0)] + pub fn commit_vote( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + vote_commit: [u8; 32], + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id)?; + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + T::SchellingGameSharedSource::commit_vote_helper_link(key, who, vote_commit)?; + Ok(()) + } + + #[pallet::call_index(7)] + #[pallet::weight(0)] + pub fn reveal_vote( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + choice: u128, + salt: Vec, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id)?; + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + T::SchellingGameSharedSource::reveal_vote_two_choice_helper_link( + key, who, choice, salt, + )?; + Ok(()) + } + + #[pallet::call_index(8)] + #[pallet::weight(0)] + pub fn get_incentives( + origin: OriginFor, + department_required_fund_id: DepartmentRequiredFundId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = + Self::get_block_number_of_schelling_game(department_required_fund_id)?; + let key = SumTreeName::DepartmentRequiredFund { + department_required_fund_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( + key, phase_data, who, + )?; + Ok(()) + } + } +} diff --git a/custom-pallets/department-funding/src/mock.rs b/custom-pallets/department-funding/src/mock.rs new file mode 100644 index 0000000..2fbd026 --- /dev/null +++ b/custom-pallets/department-funding/src/mock.rs @@ -0,0 +1,161 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, parameter_types, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; +use frame_support_test::TestRandomness; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + DepartmentFunding: pallet_template, + Balances: pallet_balances, + SharedStorage:pallet_shared_storage, + SchellingGameShared: pallet_schelling_game_shared, + SortitionSumGame: pallet_sortition_sum_game, + } +); +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type AccountData = pallet_balances::AccountData; // +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_shared_storage::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type SharedStorageSource = SharedStorage; + type Currency = Balances; // New code + type SchellingGameSharedSource = SchellingGameShared; +} + +impl pallet_balances::Config for Test { + type MaxHolds = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +impl pallet_schelling_game_shared::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; // New code + type RandomnessSource = TestRandomness; + type Slash = (); + type Reward = (); + type SortitionSumGameSource = SortitionSumGame; +} + +impl pallet_sortition_sum_game::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 100000), + (2, 200000), + (3, 300000), + (4, 300000), + (5, 300000), + (6, 300000), + (7, 300000), + (8, 300000), + (9, 300000), + (10, 300000), + (11, 300000), + (12, 300000), + (13, 300000), + (14, 300000), + (15, 300000), + (16, 300000), + (17, 300000), + (18, 300000), + (19, 300000), + (20, 300000), + (21, 300000), + (22, 300000), + (23, 300000), + (24, 300000), + (25, 300000), + (26, 300000), + (27, 300000), + (28, 300000), + (29, 300000), + (30, 300000), + (31, 300000), + (32, 300000), + (33, 300000), + (34, 300000), + (35, 300000), + ], + } // new code + .assimilate_storage(&mut t) + .unwrap(); + pallet_shared_storage::GenesisConfig:: { + approved_citizen_address: vec![1, 2], + } + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} diff --git a/custom-pallets/department-funding/src/tests.rs b/custom-pallets/department-funding/src/tests.rs new file mode 100644 index 0000000..1cf3051 --- /dev/null +++ b/custom-pallets/department-funding/src/tests.rs @@ -0,0 +1,18 @@ +use crate::{mock::*, Error, Event}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + // Dispatch a signed extrinsic. + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the expected error is thrown when no value is present. + }); +} diff --git a/pallets/department-funding/src/types.rs b/custom-pallets/department-funding/src/types.rs similarity index 62% rename from pallets/department-funding/src/types.rs rename to custom-pallets/department-funding/src/types.rs index 3486206..c20fd33 100644 --- a/pallets/department-funding/src/types.rs +++ b/custom-pallets/department-funding/src/types.rs @@ -1,6 +1,6 @@ use super::*; -use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use frame_support::pallet_prelude::*; +use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use scale_info::TypeInfo; pub const DEPARTMENT_REQUIRED_FUND_ID: DepartmentRequiredFundId = 1; @@ -11,40 +11,40 @@ pub const TIME_FOR_STAKING_FUNDING_STATUS_PASSED: u64 = (6 * 30 * 24 * 60 * 60) #[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub enum TippingName { - SmallTipper, - BigTipper, - SmallSpender, - MediumSpender, - BigSpender, + SmallTipper, + BigTipper, + SmallSpender, + MediumSpender, + BigSpender, } #[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub struct TippingValue { - pub max_tipping_value: Balance, - pub stake_required: Balance, + pub max_tipping_value: Balance, + pub stake_required: Balance, } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct DepartmentRequiredFund { - pub created: WhoAndWhenOf, - pub department_required_fund_id: DepartmentRequiredFundId, - pub department_id: DepartmentId, - pub content: Content, - pub tipping_name: TippingName, - pub funding_needed: BalanceOf, - pub creator: T::AccountId, + pub created: WhoAndWhenOf, + pub department_required_fund_id: DepartmentRequiredFundId, + pub department_id: DepartmentId, + pub content: Content, + pub tipping_name: TippingName, + pub funding_needed: BalanceOf, + pub creator: T::AccountId, } #[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub enum FundingStatus { - Processing, - Success, - Failed, + Processing, + Success, + Failed, } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub struct DepartmentFundingStatus { - pub block_number: BlockNumber, - pub status: FundingStatus, + pub block_number: BlockNumber, + pub status: FundingStatus, } diff --git a/pallets/department-funding/src/weights.rs b/custom-pallets/department-funding/src/weights.rs similarity index 100% rename from pallets/department-funding/src/weights.rs rename to custom-pallets/department-funding/src/weights.rs diff --git a/custom-pallets/election/Cargo.toml b/custom-pallets/election/Cargo.toml new file mode 100644 index 0000000..650b803 --- /dev/null +++ b/custom-pallets/election/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "pallet-election" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true , optional = true} +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-balances = { workspace = true } +sp-npos-elections = { workspace = true } +sp-runtime = { workspace = true } +log = { workspace = true } + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +frame-support-test = { workspace = true } + + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "pallet-balances/std", + "sp-npos-elections/std", + "sp-runtime/std", + "log/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/election/README.md b/custom-pallets/election/README.md similarity index 100% rename from pallets/election/README.md rename to custom-pallets/election/README.md diff --git a/custom-pallets/election/election-rpc/Cargo.toml b/custom-pallets/election/election-rpc/Cargo.toml new file mode 100644 index 0000000..659f441 --- /dev/null +++ b/custom-pallets/election/election-rpc/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "election-rpc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +parity-scale-codec = { workspace = true } +jsonrpc-core = {version = "18.0.0", features = ["arbitrary_precision"]} +jsonrpc-core-client = "18.0" +jsonrpc-derive = "18.0" +sc-rpc = { workspace = true } +sp-api = { workspace = true } +sp-blockchain ={ workspace = true } +sp-runtime = { workspace = true } + +election-runtime-api ={ path="../election-runtime-api", default-features = false} + + +[features] +default = ["std"] +std = [ + "sp-api/std", + "sp-runtime/std", + "election-runtime-api/std" +] + diff --git a/pallets/election/election-rpc/src/lib.rs b/custom-pallets/election/election-rpc/src/lib.rs similarity index 100% rename from pallets/election/election-rpc/src/lib.rs rename to custom-pallets/election/election-rpc/src/lib.rs diff --git a/pallets/election/election-runtime-api/Cargo.toml b/custom-pallets/election/election-runtime-api/Cargo.toml similarity index 50% rename from pallets/election/election-runtime-api/Cargo.toml rename to custom-pallets/election/election-runtime-api/Cargo.toml index 7d13a30..a110059 100644 --- a/pallets/election/election-runtime-api/Cargo.toml +++ b/custom-pallets/election/election-runtime-api/Cargo.toml @@ -7,8 +7,8 @@ license = "GPL-3.0-or-later" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sp-api = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42"} +sp-api = { workspace = true } +frame-support = { workspace = true } [features] default = ["std"] diff --git a/pallets/election/election-runtime-api/src/lib.rs b/custom-pallets/election/election-runtime-api/src/lib.rs similarity index 100% rename from pallets/election/election-runtime-api/src/lib.rs rename to custom-pallets/election/election-runtime-api/src/lib.rs diff --git a/pallets/department-funding/src/benchmarking.rs b/custom-pallets/election/src/benchmarking.rs similarity index 100% rename from pallets/department-funding/src/benchmarking.rs rename to custom-pallets/election/src/benchmarking.rs diff --git a/pallets/election/src/extras.rs b/custom-pallets/election/src/extras.rs similarity index 100% rename from pallets/election/src/extras.rs rename to custom-pallets/election/src/extras.rs diff --git a/pallets/election/src/lib.rs b/custom-pallets/election/src/lib.rs similarity index 100% rename from pallets/election/src/lib.rs rename to custom-pallets/election/src/lib.rs diff --git a/pallets/election/src/mock.rs b/custom-pallets/election/src/mock.rs similarity index 100% rename from pallets/election/src/mock.rs rename to custom-pallets/election/src/mock.rs diff --git a/pallets/election/src/tests.rs b/custom-pallets/election/src/tests.rs similarity index 100% rename from pallets/election/src/tests.rs rename to custom-pallets/election/src/tests.rs diff --git a/pallets/election/src/types.rs b/custom-pallets/election/src/types.rs similarity index 100% rename from pallets/election/src/types.rs rename to custom-pallets/election/src/types.rs diff --git a/pallets/election/src/weights.rs b/custom-pallets/election/src/weights.rs similarity index 100% rename from pallets/election/src/weights.rs rename to custom-pallets/election/src/weights.rs diff --git a/custom-pallets/positive-externality/Cargo.toml b/custom-pallets/positive-externality/Cargo.toml new file mode 100644 index 0000000..0cef975 --- /dev/null +++ b/custom-pallets/positive-externality/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "pallet-positive-externality" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true , optional = true} +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +pallet-timestamp = { workspace = true } +pallet-balances = { workspace = true } +pallet-support = { workspace = true } +pallet-shared-storage = { workspace = true } +trait-shared-storage = { workspace = true } +pallet-schelling-game-shared = { workspace = true } +trait-schelling-game-shared = { workspace = true } +pallet-sortition-sum-game = { workspace = true } + + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +frame-support-test = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "pallet-timestamp/std", + "pallet-balances/std", + "pallet-support/std", + "pallet-shared-storage/std", + "pallet-schelling-game-shared/std", + "pallet-sortition-sum-game/std", + "frame-support-test/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/positive-externality/README.md b/custom-pallets/positive-externality/README.md similarity index 100% rename from pallets/positive-externality/README.md rename to custom-pallets/positive-externality/README.md diff --git a/custom-pallets/positive-externality/positive-externality-rpc/Cargo.toml b/custom-pallets/positive-externality/positive-externality-rpc/Cargo.toml new file mode 100644 index 0000000..b7a0241 --- /dev/null +++ b/custom-pallets/positive-externality/positive-externality-rpc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "positive-externality-rpc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +jsonrpsee = { workspace = true } +sc-rpc = { workspace = true } +sp-api = { workspace = true } +sp-blockchain = { workspace = true } +sp-runtime = { workspace = true } +positive-externality-runtime-api = { workspace = true} \ No newline at end of file diff --git a/pallets/positive-externality/positive-externality-rpc/src/lib.rs b/custom-pallets/positive-externality/positive-externality-rpc/src/lib.rs similarity index 100% rename from pallets/positive-externality/positive-externality-rpc/src/lib.rs rename to custom-pallets/positive-externality/positive-externality-rpc/src/lib.rs diff --git a/custom-pallets/positive-externality/positive-externality-runtime-api/Cargo.toml b/custom-pallets/positive-externality/positive-externality-runtime-api/Cargo.toml new file mode 100644 index 0000000..00e9bf3 --- /dev/null +++ b/custom-pallets/positive-externality/positive-externality-runtime-api/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "positive-externality-runtime-api" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +parity-scale-codec = { workspace = true } +sp-api = { workspace = true } +frame-support = { workspace = true } +sp-std = { workspace = true} + +[features] +default = ["std"] +std = [ + "sp-api/std", + "frame-support/std", + "sp-std/std", +] \ No newline at end of file diff --git a/custom-pallets/positive-externality/positive-externality-runtime-api/src/lib.rs b/custom-pallets/positive-externality/positive-externality-runtime-api/src/lib.rs new file mode 100644 index 0000000..4425b67 --- /dev/null +++ b/custom-pallets/positive-externality/positive-externality-runtime-api/src/lib.rs @@ -0,0 +1,18 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// use frame_support::sp_std::{vec::Vec}; +// or +use parity_scale_codec::Codec; +use sp_std::prelude::*; + +sp_api::decl_runtime_apis! { + pub trait PositiveExternalityApi where AccountId: Codec { + + fn get_evidence_period_end_block(user_to_calculate: AccountId) -> Option; + fn get_staking_period_end_block(user_to_calculate: AccountId) -> Option; + fn get_drawing_period_end(user_to_calculate: AccountId) -> (u64, u64, bool); + fn get_commit_period_end_block(user_to_calculate: AccountId) -> Option; + fn get_vote_period_end_block(user_to_calculate: AccountId) -> Option; + fn selected_as_juror(user_to_calculate: AccountId, who: AccountId) -> bool; + } +} diff --git a/custom-pallets/positive-externality/src/benchmarking.rs b/custom-pallets/positive-externality/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/positive-externality/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/positive-externality/src/extras.rs b/custom-pallets/positive-externality/src/extras.rs new file mode 100644 index 0000000..6e4cac1 --- /dev/null +++ b/custom-pallets/positive-externality/src/extras.rs @@ -0,0 +1,174 @@ +use frame_support::dispatch::DispatchResult; + +use super::*; + +impl Post { + pub fn new(id: PostId, created_by: T::AccountId, content: Content) -> Self { + Post { + id, + created: new_who_and_when::(created_by.clone()), + edited: false, + owner: created_by, + content, + hidden: false, + upvotes_count: 0, + downvotes_count: 0, + } + } + + pub fn ensure_owner(&self, account: &T::AccountId) -> DispatchResult { + ensure!(self.is_owner(account), Error::::NotAPostOwner); + Ok(()) + } + + pub fn is_owner(&self, account: &T::AccountId) -> bool { + self.owner == *account + } +} + +impl Pallet { + pub(super) fn get_phase_data() -> PhaseData { + T::SchellingGameSharedSource::create_phase_data(50, 5, 3, 100, (100, 100)) + } + + pub fn ensure_validation_on_positive_externality(account: T::AccountId) -> DispatchResult { + let bool_data = Validate::::get(account); + ensure!( + bool_data == true, + Error::::ValidationPositiveExternalityIsOff + ); + + Ok(()) + } + + pub fn ensure_min_stake_positive_externality(account: T::AccountId) -> DispatchResult { + let stake = StakeBalance::::get(account); + let min_stake = MinimumStake::::get(); + // println!("stake {:?}", stake); + // println!("min stake {:?}", min_stake); + ensure!(stake >= min_stake, Error::::LessThanMinStake); + + Ok(()) + } + + pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { + input.saturated_into::>() + } + + pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { + input.saturated_into::>() + } + + pub(super) fn get_drawn_jurors(user_to_calculate: T::AccountId) -> Vec<(T::AccountId, u64)> { + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + T::SchellingGameSharedSource::get_drawn_jurors(key) + } + + // Block code start + + pub fn get_evidence_period_end_block(user_to_calculate: T::AccountId) -> Option { + let now = >::block_number(); + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_staking_period_end_block(user_to_calculate: T::AccountId) -> Option { + let now = >::block_number(); + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_drawing_period_end(user_to_calculate: T::AccountId) -> (u64, u64, bool) { + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + let phase_data = Self::get_phase_data(); + + let result = + T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); + result + } + + pub fn get_commit_period_end_block(user_to_calculate: T::AccountId) -> Option { + let now = >::block_number(); + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_vote_period_end_block(user_to_calculate: T::AccountId) -> Option { + let now = >::block_number(); + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn selected_as_juror(user_to_calculate: T::AccountId, who: T::AccountId) -> bool { + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); + result + } + + // Block code end +} diff --git a/custom-pallets/positive-externality/src/lib.rs b/custom-pallets/positive-externality/src/lib.rs new file mode 100644 index 0000000..acaa646 --- /dev/null +++ b/custom-pallets/positive-externality/src/lib.rs @@ -0,0 +1,419 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +mod extras; +pub mod types; +pub use types::{Post, FIRST_POST_ID}; + +use frame_support::pallet_prelude::DispatchError; +use frame_support::pallet_prelude::*; +use frame_support::sp_runtime::traits::Saturating; +use frame_support::sp_runtime::SaturatedConversion; +use frame_support::{dispatch::DispatchResult, ensure}; +use frame_support::{ + traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, + PalletId, +}; +use frame_system::pallet_prelude::*; +use pallet_schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType}; +use pallet_sortition_sum_game::types::SumTreeName; +use pallet_support::{ + ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, PostId, WhoAndWhen, + WhoAndWhenOf, +}; +use sp_std::prelude::*; +use trait_schelling_game_shared::SchellingGameSharedLink; +use trait_shared_storage::SharedStorageLink; +type AccountIdOf = ::AccountId; +type BalanceOf = <::Currency as Currency>>::Balance; +pub type BlockNumberOf = BlockNumberFor; +pub type SumTreeNameType = SumTreeName, BlockNumberOf>; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_timestamp::Config + pallet_schelling_game_shared::Config + { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + + type SharedStorageSource: SharedStorageLink>; + type SchellingGameSharedSource: SchellingGameSharedLink< + SumTreeName = SumTreeName>, + SchellingGameType = SchellingGameType, + BlockNumber = BlockNumberOf, + AccountId = AccountIdOf, + Balance = BalanceOf, + RangePoint = RangePoint, + Period = Period, + PhaseData = PhaseData, + >; + type Currency: ReservableCurrency; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + #[pallet::type_value] + pub fn DefaultForNextPostId() -> PostId { + FIRST_POST_ID + } + + /// The next post id. + #[pallet::storage] + #[pallet::getter(fn next_post_id)] + pub type NextPostId = StorageValue<_, PostId, ValueQuery, DefaultForNextPostId>; + + /// Get the details of a post by its' id. + #[pallet::storage] + #[pallet::getter(fn post_by_id)] + pub type PostById = StorageMap<_, Twox64Concat, PostId, Post>; + + #[pallet::storage] + #[pallet::getter(fn evidence)] + pub type Evidence = + StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + + #[pallet::type_value] + pub fn MinimumStake() -> BalanceOf { + 10000u128.saturated_into::>() + } + + #[pallet::storage] + #[pallet::getter(fn user_stake)] + pub type StakeBalance = + StorageMap<_, Twox64Concat, T::AccountId, BalanceOf, ValueQuery>; + + #[pallet::type_value] + pub fn DefaultValidate() -> bool { + true + } + + #[pallet::storage] + #[pallet::getter(fn validate)] + pub type Validate = + StorageMap<_, Twox64Concat, T::AccountId, bool, ValueQuery, DefaultValidate>; + + #[pallet::storage] + #[pallet::getter(fn validation_block)] + pub type ValidationBlock = + StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberOf, ValueQuery>; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + NotAPostOwner, + ValidationPositiveExternalityIsOff, + LessThanMinStake, + CannotStakeNow, + ChoiceOutOfRange, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn create_positive_externality_post( + origin: OriginFor, + content: Content, + ) -> DispatchResult { + let creator = ensure_signed(origin)?; + + ensure_content_is_valid(content.clone())?; + + // Citizen approved To comment out in production, citizen approved in added in profile validation + + // T::SharedStorageSource::check_citizen_is_approved_link(creator.clone())?; + + let new_post_id = Self::next_post_id(); + + let new_post: Post = Post::new(new_post_id, creator.clone(), content.clone()); + + Evidence::::mutate(creator, |ids| ids.push(new_post_id)); + + PostById::insert(new_post_id, new_post); + NextPostId::::mutate(|n| { + *n += 1; + }); + + // emit event + + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn set_validate_positive_externality( + origin: OriginFor, + value: bool, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + // Check user has done kyc + + Validate::::insert(&who, value); + // emit event + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(0)] + pub fn apply_staking_period( + origin: OriginFor, + user_to_calculate: T::AccountId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::ensure_validation_on_positive_externality(user_to_calculate.clone())?; + + let stake = MinimumStake::::get(); + + let _ = ::Currency::withdraw( + &who, + stake, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; + + StakeBalance::::insert(&user_to_calculate, stake); + + let pe_block_number = >::get(user_to_calculate.clone()); + // println!("{:?}", pe_block_number); + let zero_block_number = Self::u64_to_block_saturated(0); + let now = >::block_number(); + let three_month_number = (3 * 30 * 24 * 60 * 60) / 6; + let three_month_block = Self::u64_to_block_saturated(three_month_number); + let modulus = now % three_month_block; + let storage_main_block = now - modulus; + // println!("{:?}", now); + // println!("{:?}", three_month_number); + // println!("{:?}", storage_main_block); + // println!("{:?}", pe_block_number); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate.clone(), + block_number: storage_main_block.clone(), + }; + + // let game_type = SchellingGameType::PositiveExternality; + // || pe_block_number == zero_block_number + + if storage_main_block > pe_block_number || pe_block_number == zero_block_number { + >::insert(user_to_calculate.clone(), storage_main_block); + // check what if called again + T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now)?; + T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; + + // println!("{:?}", data); + } else { + return Err(Error::::CannotStakeNow.into()); + } + + Ok(()) + } + + #[pallet::call_index(3)] + #[pallet::weight(0)] + pub fn apply_jurors( + origin: OriginFor, + user_to_calculate: T::AccountId, + stake: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::ensure_validation_on_positive_externality(user_to_calculate.clone())?; + Self::ensure_min_stake_positive_externality(user_to_calculate.clone())?; + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::apply_jurors_helper_link(key, phase_data, who, stake)?; + + Ok(()) + } + + #[pallet::call_index(4)] + #[pallet::weight(0)] + pub fn pass_period( + origin: OriginFor, + user_to_calculate: T::AccountId, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let now = >::block_number(); + let phase_data = Self::get_phase_data(); + T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; + + Ok(()) + } + + #[pallet::call_index(5)] + #[pallet::weight(0)] + pub fn draw_jurors( + origin: OriginFor, + user_to_calculate: T::AccountId, + iterations: u64, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; + + Ok(()) + } + + // Unstaking + // Stop drawn juror to unstake ✔️ + #[pallet::call_index(6)] + #[pallet::weight(0)] + pub fn unstaking(origin: OriginFor, user_to_calculate: T::AccountId) -> DispatchResult { + let who = ensure_signed(origin)?; + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; + Ok(()) + } + + #[pallet::call_index(7)] + #[pallet::weight(0)] + pub fn commit_vote( + origin: OriginFor, + user_to_calculate: T::AccountId, + vote_commit: [u8; 32], + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + T::SchellingGameSharedSource::commit_vote_for_score_helper_link(key, who, vote_commit)?; + Ok(()) + } + + #[pallet::call_index(8)] + #[pallet::weight(0)] + pub fn reveal_vote( + origin: OriginFor, + user_to_calculate: T::AccountId, + choice: i64, + salt: Vec, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + ensure!(choice <= 5 && choice >= 1, Error::::ChoiceOutOfRange); + + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate, + block_number: pe_block_number.clone(), + }; + + T::SchellingGameSharedSource::reveal_vote_score_helper_link(key, who, choice, salt)?; + Ok(()) + } + + #[pallet::call_index(9)] + #[pallet::weight(0)] + pub fn get_incentives( + origin: OriginFor, + user_to_calculate: T::AccountId, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + let pe_block_number = >::get(user_to_calculate.clone()); + + let key = SumTreeName::PositiveExternality { + user_address: user_to_calculate.clone(), + block_number: pe_block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + T::SchellingGameSharedSource::get_incentives_score_schelling_helper_link( + key.clone(), + phase_data, + RangePoint::ZeroToFive, + )?; + + let score = T::SchellingGameSharedSource::get_mean_value_link(key.clone())?; + // println!("Score {:?}", score); + T::SharedStorageSource::set_positive_externality_link(user_to_calculate, score)?; + + Ok(()) + } + } +} diff --git a/custom-pallets/positive-externality/src/mock.rs b/custom-pallets/positive-externality/src/mock.rs new file mode 100644 index 0000000..9b1418b --- /dev/null +++ b/custom-pallets/positive-externality/src/mock.rs @@ -0,0 +1,163 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, parameter_types, + traits::{ConstU16, ConstU64}, +}; +use frame_support_test::TestRandomness; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + TemplateModule: pallet_template, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + SharedStorage:pallet_shared_storage, + SchellingGameShared: pallet_schelling_game_shared, + SortitionSumGame: pallet_sortition_sum_game, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type AccountData = pallet_balances::AccountData; // N +} + +impl pallet_shared_storage::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_balances::Config for Test { + type MaxHolds = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +impl pallet_schelling_game_shared::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; // New code + type RandomnessSource = TestRandomness; + type Slash = (); + type Reward = (); + type SortitionSumGameSource = SortitionSumGame; +} + +impl pallet_sortition_sum_game::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type SharedStorageSource = SharedStorage; + type Currency = Balances; // New code + type SchellingGameSharedSource = SchellingGameShared; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 100000), + (2, 200000), + (3, 300000), + (4, 300000), + (5, 300000), + (6, 300000), + (7, 300000), + (8, 300000), + (9, 300000), + (10, 300000), + (11, 300000), + (12, 300000), + (13, 300000), + (14, 300000), + (15, 300000), + (16, 300000), + (17, 300000), + (18, 300000), + (19, 300000), + (20, 300000), + (21, 300000), + (22, 300000), + (23, 300000), + (24, 300000), + (25, 300000), + (26, 300000), + (27, 300000), + (28, 300000), + (29, 300000), + (30, 300000), + (31, 300000), + (32, 300000), + (33, 300000), + (34, 300000), + (35, 300000), + ], + } // new code + .assimilate_storage(&mut t) + .unwrap(); + pallet_shared_storage::GenesisConfig:: { + approved_citizen_address: vec![1, 2], + } + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} diff --git a/custom-pallets/positive-externality/src/tests.rs b/custom-pallets/positive-externality/src/tests.rs new file mode 100644 index 0000000..547ef04 --- /dev/null +++ b/custom-pallets/positive-externality/src/tests.rs @@ -0,0 +1,343 @@ +use crate::types::Post; +use crate::{mock::*, Error, Event}; +use frame_support::{assert_noop, assert_ok}; +use pallet_support::{Content, WhoAndWhen}; + +#[test] +fn test_positive_externality_post() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::create_positive_externality_post( + RuntimeOrigin::signed(1), + Content::None + )); + let post = TemplateModule::post_by_id(1); + let post_compare = Some(Post { + id: 1, + created: WhoAndWhen { + account: 1, + block: 0, + time: 0, + }, + edited: false, + owner: 1, + content: Content::None, + hidden: false, + upvotes_count: 0, + downvotes_count: 0, + }); + assert_eq!(post, post_compare); + // assert_ok!(TemplateModule::apply_jurors(Origin::signed(1), 2, 60)); + }); +} + +#[test] +fn test_setting_positive_externality_validation() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::set_validate_positive_externality( + RuntimeOrigin::signed(1), + true + )); + let value = TemplateModule::validate(1); + assert_eq!(value, true); + }); +} + +#[test] +fn test_applying_for_staking_period() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::set_validate_positive_externality( + RuntimeOrigin::signed(1), + true + )); + System::set_block_number(1298000); + assert_ok!(TemplateModule::apply_staking_period( + RuntimeOrigin::signed(2), + 1 + )); + System::set_block_number(1298000 + 1298000); + assert_ok!(TemplateModule::apply_staking_period( + RuntimeOrigin::signed(2), + 1 + )); + }); +} + +#[test] +fn test_appying_jurors() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::set_validate_positive_externality( + RuntimeOrigin::signed(1), + true + )); + // System::set_block_number(1298000); + assert_ok!(TemplateModule::apply_staking_period( + RuntimeOrigin::signed(2), + 1 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(4), + 1, + 1000 + )); + }); +} + +#[test] +fn test_change_period() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::set_validate_positive_externality( + RuntimeOrigin::signed(1), + true + )); + System::set_block_number(1298000); + assert_ok!(TemplateModule::apply_staking_period( + RuntimeOrigin::signed(2), + 1 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(4), + 1, + 1000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(5), + 1, + 2000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(6), + 1, + 3000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(7), + 1, + 4000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(8), + 1, + 5000 + )); + System::set_block_number(1298080); + assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); + }) +} + +#[test] +fn test_draw_jurors_period() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::set_validate_positive_externality( + RuntimeOrigin::signed(1), + true + )); + System::set_block_number(1298000); + assert_ok!(TemplateModule::apply_staking_period( + RuntimeOrigin::signed(2), + 1 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(4), + 1, + 1000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(5), + 1, + 2000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(6), + 1, + 3000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(7), + 1, + 4000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(8), + 1, + 5000 + )); + System::set_block_number(1298080); + assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); + assert_ok!(TemplateModule::draw_jurors(RuntimeOrigin::signed(8), 1, 5)); + }) +} + +#[test] +fn test_drawn_jurors() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::set_validate_positive_externality( + RuntimeOrigin::signed(1), + true + )); + System::set_block_number(1298000); + assert_ok!(TemplateModule::apply_staking_period( + RuntimeOrigin::signed(2), + 1 + )); + let balance = Balances::free_balance(4); + assert_eq!(300000, balance); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(4), + 1, + 1000 + )); + let balance = Balances::free_balance(4); + assert_eq!(299000, balance); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(5), + 1, + 2000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(6), + 1, + 3000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(7), + 1, + 4000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(8), + 1, + 5000 + )); + System::set_block_number(1298080); + assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); + assert_ok!(TemplateModule::draw_jurors(RuntimeOrigin::signed(8), 1, 5)); + let data = TemplateModule::get_drawn_jurors(1); + assert_eq!( + data, + [(4, 1000), (5, 2000), (6, 3000), (7, 4000), (8, 5000)] + ); + // println!("drawn jurors {:?}",data); + }) +} + +#[test] +fn test_commit_and_incentives_vote() { + new_test_ext().execute_with(|| { + assert_ok!(TemplateModule::set_validate_positive_externality( + RuntimeOrigin::signed(1), + true + )); + System::set_block_number(1298000); + assert_ok!(TemplateModule::apply_staking_period( + RuntimeOrigin::signed(2), + 1 + )); + let balance = Balances::free_balance(4); + assert_eq!(300000, balance); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(4), + 1, + 1000 + )); + let balance = Balances::free_balance(4); + assert_eq!(299000, balance); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(5), + 1, + 2000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(6), + 1, + 3000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(7), + 1, + 4000 + )); + assert_ok!(TemplateModule::apply_jurors( + RuntimeOrigin::signed(8), + 1, + 5000 + )); + System::set_block_number(1298080); + assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); + assert_ok!(TemplateModule::draw_jurors(RuntimeOrigin::signed(8), 1, 5)); + + let data = TemplateModule::get_drawn_jurors(1); + assert_eq!( + data, + [(4, 1000), (5, 2000), (6, 3000), (7, 4000), (8, 5000)] + ); + assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); + + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(TemplateModule::commit_vote( + RuntimeOrigin::signed(4), + 1, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(TemplateModule::commit_vote( + RuntimeOrigin::signed(5), + 1, + hash + )); + let hash = sp_io::hashing::keccak_256("5salt3".as_bytes()); + assert_ok!(TemplateModule::commit_vote( + RuntimeOrigin::signed(6), + 1, + hash + )); + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(TemplateModule::commit_vote( + RuntimeOrigin::signed(7), + 1, + hash + )); + let hash = sp_io::hashing::keccak_256("5salt5".as_bytes()); + assert_ok!(TemplateModule::commit_vote( + RuntimeOrigin::signed(8), + 1, + hash + )); + System::set_block_number(12980160); + assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); + assert_ok!(TemplateModule::reveal_vote( + RuntimeOrigin::signed(4), + 1, + 1, + "salt".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote( + RuntimeOrigin::signed(5), + 1, + 1, + "salt2".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote( + RuntimeOrigin::signed(6), + 1, + 5, + "salt3".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote( + RuntimeOrigin::signed(7), + 1, + 1, + "salt4".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote( + RuntimeOrigin::signed(8), + 1, + 5, + "salt5".as_bytes().to_vec() + )); + System::set_block_number(12980260); + assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); + + assert_ok!(TemplateModule::get_incentives(RuntimeOrigin::signed(4), 1)); + }) +} diff --git a/pallets/positive-externality/src/types.rs b/custom-pallets/positive-externality/src/types.rs similarity index 59% rename from pallets/positive-externality/src/types.rs rename to custom-pallets/positive-externality/src/types.rs index 9f3ebaa..39ca8da 100644 --- a/pallets/positive-externality/src/types.rs +++ b/custom-pallets/positive-externality/src/types.rs @@ -1,4 +1,4 @@ -use codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use frame_support::pallet_prelude::*; @@ -11,25 +11,25 @@ pub const FIRST_POST_ID: u64 = 1; #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct Post { - pub id: PostId, + pub id: PostId, - pub created: WhoAndWhenOf, + pub created: WhoAndWhenOf, - pub edited: bool, + pub edited: bool, - pub owner: T::AccountId, + pub owner: T::AccountId, - pub content: Content, + pub content: Content, - pub hidden: bool, + pub hidden: bool, - pub upvotes_count: u32, + pub upvotes_count: u32, - pub downvotes_count: u32, + pub downvotes_count: u32, } #[derive(Encode, Decode, Default, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub struct PositiveExternalityPostUpdate { - pub content: Option, - pub hidden: Option, + pub content: Option, + pub hidden: Option, } diff --git a/pallets/positive-externality/src/weights.rs b/custom-pallets/positive-externality/src/weights.rs similarity index 100% rename from pallets/positive-externality/src/weights.rs rename to custom-pallets/positive-externality/src/weights.rs diff --git a/custom-pallets/profile-validation/Cargo.toml b/custom-pallets/profile-validation/Cargo.toml new file mode 100644 index 0000000..39015fd --- /dev/null +++ b/custom-pallets/profile-validation/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "pallet-profile-validation" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +pallet-timestamp = { workspace = true } +pallet-balances = { workspace = true } +pallet-support = { workspace = true } +pallet-schelling-game-shared = { workspace = true } +pallet-sortition-sum-game = { workspace = true} +trait-schelling-game-shared= { workspace = true } + + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +frame-support-test = { workspace = true } + + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-std/std", + "pallet-timestamp/std", + "pallet-balances/std", + "pallet-support/std", + "pallet-schelling-game-shared/std", + "pallet-sortition-sum-game/std", + "frame-support-test/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/posts/README.md b/custom-pallets/profile-validation/README.md similarity index 100% rename from pallets/posts/README.md rename to custom-pallets/profile-validation/README.md diff --git a/custom-pallets/profile-validation/profile-validation-rpc/Cargo.toml b/custom-pallets/profile-validation/profile-validation-rpc/Cargo.toml new file mode 100644 index 0000000..225bf41 --- /dev/null +++ b/custom-pallets/profile-validation/profile-validation-rpc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "profile-validation-rpc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +jsonrpsee = { workspace = true } +sc-rpc = { workspace = true } +sp-api = { workspace = true } +sp-blockchain = { workspace = true } +sp-runtime = { workspace = true } +profile-validation-runtime-api = { workspace = true } diff --git a/pallets/profile-validation/profile-validation-rpc/src/lib.rs b/custom-pallets/profile-validation/profile-validation-rpc/src/lib.rs similarity index 100% rename from pallets/profile-validation/profile-validation-rpc/src/lib.rs rename to custom-pallets/profile-validation/profile-validation-rpc/src/lib.rs diff --git a/custom-pallets/profile-validation/profile-validation-runtime-api/Cargo.toml b/custom-pallets/profile-validation/profile-validation-runtime-api/Cargo.toml new file mode 100644 index 0000000..64cf80e --- /dev/null +++ b/custom-pallets/profile-validation/profile-validation-runtime-api/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "profile-validation-runtime-api" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +parity-scale-codec = { workspace = true } +sp-api = { workspace = true } +frame-support = { workspace = true } +sp-std = { workspace = true} + +[features] +default = ["std"] +std = [ + "sp-api/std", + "frame-support/std", + "sp-std/std", +] \ No newline at end of file diff --git a/custom-pallets/profile-validation/profile-validation-runtime-api/src/lib.rs b/custom-pallets/profile-validation/profile-validation-runtime-api/src/lib.rs new file mode 100644 index 0000000..79c0dac --- /dev/null +++ b/custom-pallets/profile-validation/profile-validation-runtime-api/src/lib.rs @@ -0,0 +1,22 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// use frame_support::sp_std::{vec::Vec}; +// or +use parity_scale_codec::Codec; +use sp_std::prelude::*; + +type ChallengePostId = u64; + +sp_api::decl_runtime_apis! { + pub trait ProfileValidationApi where AccountId: Codec { + + fn get_challengers_evidence(profile_user_account: AccountId, offset: u64, limit: u16) -> Vec; + + fn get_evidence_period_end_block(profile_user_account: AccountId) -> Option; + fn get_staking_period_end_block(profile_user_account: AccountId) -> Option; + fn get_drawing_period_end(profile_user_account: AccountId) -> (u64, u64, bool); + fn get_commit_period_end_block(profile_user_account: AccountId) -> Option; + fn get_vote_period_end_block(profile_user_account: AccountId) -> Option; + fn selected_as_juror(profile_user_account: AccountId, who: AccountId) -> bool; + } +} diff --git a/custom-pallets/profile-validation/src/benchmarking.rs b/custom-pallets/profile-validation/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/profile-validation/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/profile-validation/src/docimages/change_period.svg b/custom-pallets/profile-validation/src/docimages/change_period.svg similarity index 100% rename from pallets/profile-validation/src/docimages/change_period.svg rename to custom-pallets/profile-validation/src/docimages/change_period.svg diff --git a/custom-pallets/profile-validation/src/extras.rs b/custom-pallets/profile-validation/src/extras.rs new file mode 100644 index 0000000..542bb0c --- /dev/null +++ b/custom-pallets/profile-validation/src/extras.rs @@ -0,0 +1,209 @@ +use crate::*; + +impl CitizenDetailsPost { + pub fn new(citizen_id: CitizenId, created_by: T::AccountId, content: Content) -> Self { + CitizenDetailsPost { + created: new_who_and_when::(created_by.clone()), + content, + citizen_id, + owner: created_by, + edited: false, + hidden: false, + upvotes_count: 0, + downvotes_count: 0, + } + } + + pub fn ensure_owner(&self, account: &T::AccountId) -> DispatchResult { + ensure!(self.is_owner(account), Error::::NotAPostOwner); + Ok(()) + } + + pub fn is_owner(&self, account: &T::AccountId) -> bool { + self.owner == *account + } +} + +impl ChallengeEvidencePost { + pub fn new( + kyc_profile_id: T::AccountId, + created_by: T::AccountId, + content: Content, + post_id_if_comment: Option, + ) -> Self { + ChallengeEvidencePost { + created: new_who_and_when::(created_by.clone()), + owner: created_by, + kyc_profile_id, + content, + post_id_if_comment, + is_comment: false, + } + } + + pub fn ensure_owner(&self, account: &T::AccountId) -> DispatchResult { + ensure!(self.is_owner(account), Error::::NotAPostOwner); + Ok(()) + } + + pub fn is_owner(&self, account: &T::AccountId) -> bool { + self.owner == *account + } +} + +impl Pallet { + pub(super) fn get_phase_data() -> PhaseData { + T::SchellingGameSharedSource::create_phase_with_all_data( + 10, + 100, + 100, + 100, + 100, + 100, + 100, + 5, + 5, + 100, + (100, 100), + ) + // T::SchellingGameSharedSource::create_phase_data(100, 5, 3, 100, (100, 100)) + } + + // pub(super) fn get_citizen_accountid( + // citizenid: CitizenId, + // ) -> Result { + // let profile = Self::citizen_profile(citizenid).ok_or(Error::::CitizenDoNotExists)?; + // Ok(profile.owner) + // } + + pub(super) fn fund_profile_account() -> T::AccountId { + PALLET_ID.into_sub_account_truncating(1) + } + + pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { + input.saturated_into::>() + } + + pub(super) fn balance_to_u64_saturated(input: BalanceOf) -> u64 { + input.saturated_into::() + } + + pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { + input.saturated_into::>() + } + + pub fn get_challengers_evidence( + profile_user_account: T::AccountId, + offset: u64, + limit: u16, + ) -> Vec { + let mut data = >::iter_prefix_values(&profile_user_account) + .skip(offset as usize) + .take(limit as usize) + .collect::>(); + data.sort(); + data.reverse(); + data + } + + pub fn get_evidence_period_end_block(profile_user_account: T::AccountId) -> Option { + let now = >::block_number(); + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_staking_period_end_block(profile_user_account: T::AccountId) -> Option { + let now = >::block_number(); + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_drawing_period_end(profile_user_account: T::AccountId) -> (u64, u64, bool) { + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + let phase_data = Self::get_phase_data(); + + let result = + T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); + result + } + + pub fn get_commit_period_end_block(profile_user_account: T::AccountId) -> Option { + let now = >::block_number(); + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_vote_period_end_block(profile_user_account: T::AccountId) -> Option { + let now = >::block_number(); + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn selected_as_juror(profile_user_account: T::AccountId, who: T::AccountId) -> bool { + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + + let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); + result + } + + pub fn profile_fund_required(profile_user_account: T::AccountId) -> Option { + let registration_fee = Self::profile_registration_challenge_fees(); + let total_funded = Self::total_fund_for_profile_collected(profile_user_account); + let registration_fee_u64 = Self::balance_to_u64_saturated(registration_fee); + let total_fund_u64 = Self::balance_to_u64_saturated(total_funded); + let fund_required = registration_fee_u64.checked_sub(total_fund_u64); + fund_required + } +} diff --git a/custom-pallets/profile-validation/src/lib.rs b/custom-pallets/profile-validation/src/lib.rs new file mode 100644 index 0000000..389cbbe --- /dev/null +++ b/custom-pallets/profile-validation/src/lib.rs @@ -0,0 +1,901 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +/// +/// To Do: +/// Add profile ✅ +/// Crowdfund for profile stake ✅ +/// Add another account in case you loose account access +/// Appeal in case of fradulent account +/// Clean the storage after are incentives are given +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +mod extras; +mod permissions; +mod types; + +use crate::types::{ChallengeEvidencePost, ChallengerFundInfo, ProfileFundInfo}; +use frame_support::sp_runtime::traits::AccountIdConversion; +use frame_support::sp_runtime::traits::{CheckedAdd, CheckedSub}; +use frame_support::sp_runtime::SaturatedConversion; +use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; +use frame_support::{ + traits::{ + Currency, ExistenceRequirement, Get, OnUnbalanced, ReservableCurrency, WithdrawReasons, + }, + PalletId, +}; +use frame_system::pallet_prelude::*; +use sp_std::prelude::*; + +use pallet_schelling_game_shared::types::{ + Period, PhaseData, RangePoint, SchellingGameType, WinningDecision, +}; +use pallet_sortition_sum_game::types::SumTreeName; +use pallet_support::{ + ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, WhoAndWhen, WhoAndWhenOf, +}; +use trait_schelling_game_shared::SchellingGameSharedLink; +pub use types::{CitizenDetailsPost, FIRST_CHALLENGE_POST_ID, FIRST_CITIZEN_ID}; +type AccountIdOf = ::AccountId; +type BalanceOf = <::Currency as Currency>>::Balance; +type ProfileFundInfoOf = ProfileFundInfo, AccountIdOf>; +type ChallengerFundInfoOf = ChallengerFundInfo, BlockNumberFor, AccountIdOf>; +pub type BlockNumberOf = BlockNumberFor; +type CitizenId = u64; +type ChallengePostId = u64; +type PositiveImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::PositiveImbalance; +type NegativeImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::NegativeImbalance; + +const PALLET_ID: PalletId = PalletId(*b"ex/cfund"); + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_timestamp::Config + pallet_schelling_game_shared::Config + { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + + type SchellingGameSharedSource: SchellingGameSharedLink< + SumTreeName = SumTreeName>, + SchellingGameType = SchellingGameType, + BlockNumber = BlockNumberOf, + AccountId = AccountIdOf, + Balance = BalanceOf, + RangePoint = RangePoint, + Period = Period, + WinningDecision = WinningDecision, + PhaseData = PhaseData, + >; + + type Currency: ReservableCurrency; + /// Handler for the unbalanced increment when rewarding (minting rewards) + type Reward: OnUnbalanced>; + + /// Handler for the unbalanced decrement when slashing (burning collateral) + type Slash: OnUnbalanced>; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + #[pallet::type_value] + pub fn DefaultForNextCitizenId() -> CitizenId { + FIRST_CITIZEN_ID + } + + #[pallet::storage] + #[pallet::getter(fn next_citizen_id)] + pub type NextCitizenId = + StorageValue<_, CitizenId, ValueQuery, DefaultForNextCitizenId>; + + #[pallet::storage] + #[pallet::getter(fn get_citizen_id)] + pub type GetCitizenId = StorageMap<_, Blake2_128Concat, T::AccountId, CitizenId>; + + #[pallet::storage] + #[pallet::getter(fn citizen_profile)] + pub type CitizenProfile = + StorageMap<_, Blake2_128Concat, T::AccountId, CitizenDetailsPost>; // Peer account id => Peer Profile Hash + + // Registration Fees + + #[pallet::type_value] + pub fn DefaultRegistrationFee() -> BalanceOf { + 1000u128.saturated_into::>() + } + // Registration challenge fees + #[pallet::type_value] + pub fn DefaultRegistrationChallengeFee() -> BalanceOf { + 100u128.saturated_into::>() + } + + #[pallet::storage] + #[pallet::getter(fn profile_registration_fees)] + pub type RegistrationFee = + StorageValue<_, BalanceOf, ValueQuery, DefaultRegistrationFee>; + + #[pallet::storage] + #[pallet::getter(fn profile_registration_challenge_fees)] + pub type RegistrationChallengeFee = + StorageValue<_, BalanceOf, ValueQuery, DefaultRegistrationChallengeFee>; + + #[pallet::storage] + #[pallet::getter(fn profile_fund_details)] + pub type ProfileFundDetails = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Blake2_128Concat, + T::AccountId, + ProfileFundInfoOf, + >; // Profile account id and (funder accountid, profile fund info) + + #[pallet::storage] + #[pallet::getter(fn total_fund_for_profile_collected)] + pub type ProfileTotalFundCollected = + StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn validation_block)] + pub type ValidationBlock = + StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn challenger_fund)] + pub type ChallengerFundDetails = + StorageMap<_, Blake2_128Concat, T::AccountId, ChallengerFundInfoOf>; // Profile account id and challenger fund info + + /// There is a single challenger, but they can have multiple posts + #[pallet::storage] + #[pallet::getter(fn challenger_evidence_query)] + pub type ChallengerEvidenceId = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Blake2_128Concat, + T::AccountId, + ChallengePostId, + >; // profile accountid, challenger accountid => Challenge post id + + #[pallet::type_value] + pub fn DefaultForNextChallengePostId() -> ChallengePostId { + FIRST_CHALLENGE_POST_ID + } + + #[pallet::storage] + #[pallet::getter(fn next_challenge_post_count)] + pub type NextChallengePostId = + StorageValue<_, ChallengePostId, ValueQuery, DefaultForNextChallengePostId>; + + #[pallet::storage] + #[pallet::getter(fn challenge_post_comment)] + pub type ChallengePostCommentIds = + StorageMap<_, Blake2_128Concat, ChallengePostId, Vec, ValueQuery>; // challenge post id => Vec + + #[pallet::storage] + #[pallet::getter(fn challenge_post)] + pub type ChallengePost = + StorageMap<_, Blake2_128Concat, ChallengePostId, ChallengeEvidencePost>; // challenge post id => post + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { + something: u32, + who: T::AccountId, + }, + CreateCitizen(T::AccountId, CitizenId), + ProfileFund { + profile: T::AccountId, + funder: T::AccountId, + }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + NoMoreUpdates, + CitizenDoNotExists, + ProfileFundExists, + PostAlreadyExists, + ProfileIsAlreadyValidated, + ChallengeExits, + ChallengeDoesNotExists, + CommentExists, + IsComment, + ProfileFundNotExists, + ChallengerFundInfoExists, + NotProfileUser, + NotEvidencePeriod, + CitizenNotApproved, + NotAPostOwner, + AmountFundedGreaterThanRequired, + ProfileFundAlreadyReturned, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + /// Add citizen + ///

+        /// Get the count from NextCitizenId
+        /// If CitizenId exists update the content, only if `ProfileTotalFundCollected` is zero
+        /// If CitizenId doesn't exists insert the content, and increment the `NextCitizenId`
+        /// 
+ /// #[pallet::weight(::WeightInfo::add_citizen())] + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn add_citizen(origin: OriginFor, content: Content) -> DispatchResult { + let who = ensure_signed(origin)?; + let count = Self::next_citizen_id(); + match >::get(&who) { + Some(citizen_id) => { + let total_funded = >::get(who.clone()); + if total_funded == 0u128.saturated_into::>() { + let new_post: CitizenDetailsPost = + CitizenDetailsPost::new(citizen_id, who.clone(), content.clone()); + >::insert(who.clone(), new_post); + Ok(()) + } else { + Err(Error::::NoMoreUpdates)? + } + } + None => { + >::insert(&who, count); + + let new_post: CitizenDetailsPost = + CitizenDetailsPost::new(count, who.clone(), content.clone()); + + >::insert(who.clone(), new_post); + NextCitizenId::::mutate(|n| { + *n += 1; + }); + Self::deposit_event(Event::CreateCitizen(who, count)); + Ok(()) + } + } + } + + /// # Crowdfunding of Profile + /// + /// Allows users to contribute funds to a profile and manages the associated data for crowdfunding. + /// + /// ## Parameters + /// + /// - `origin`: The origin of the transaction. + /// - `profile_user_account`: The account ID of the profile to fund. + /// - `amount_to_fund`: The amount of funds to be added to the profile's crowdfunding. + /// + /// ## Errors + /// + /// This function can return an error if the amount to fund is greater than the required fund. + /// + /// ## Storage + /// + /// - `ValidationBlock`: Stores the block number to be used for the profile's validation when `amount_to_fund` equals `required_fund`. + /// - `ProfileFundDetails`: Stores details of funds deposited by users for a specific profile. + /// - `ProfileTotalFundCollected`: Keeps track of the total funds collected for each profile. + /// - `RegistrationFee`: Retrieves the registration fee required for profile validation. + /// - `GetCitizenId`: Storage map that associates a citizen's account address with their Citizen ID. + /// + /// ## Usage + /// + /// Call this function to contribute funds to a profile and update the associated storage items. + /// Checks are performed to ensure the profile exists and that the funded amount is not greater + /// than required. If the funded amount matches the required amount, the profile validation is marked + /// as completed, and a link is set to the evidence period in the Schelling Game. + /// + /// ```rust,ignore + /// #[pallet::call] + /// fn add_profile_stake( + /// origin: OriginFor, + /// profile_user_account: T::AccountId, + /// amount_to_fund: BalanceOf, + /// ) -> DispatchResult { + /// // implementation + /// } + /// ``` + + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn add_profile_stake( + origin: OriginFor, + profile_user_account: T::AccountId, + amount_to_fund: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Ensure that the `profile_user_account`` exists in `GetCitizenId` storage. + Self::ensure_account_id_has_profile(profile_user_account.clone())?; + + // Retrieve the registration fee required for profile validation. + let registration_fee = >::get(); + + // Get the total funds already collected for the profile. + let total_funded = >::get(profile_user_account.clone()); + + // Calculate the required fund by subtracting the total funded from the registration fee. + let required_fund = registration_fee + .checked_sub(&total_funded) + .expect("Overflow"); + + // Check if the amount_to_fund is less than or equal to the required fund. + if amount_to_fund <= required_fund { + if amount_to_fund == required_fund { + // If the funded amount matches the required amount, update variables required for profile validation. + let now = >::block_number(); + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number: now.clone(), + }; + >::insert(&profile_user_account, now); + + // Set a link to the evidence period in the Schelling Game. + T::SchellingGameSharedSource::set_to_evidence_period_link(key, now)?; + } + + // Withdraw funds from the funder's account. + let _ = ::Currency::withdraw( + &who, + amount_to_fund.clone(), + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; + + // Update the profile fund details for the funder. + match >::get(profile_user_account.clone(), who.clone()) { + Some(mut profile_fund_info) => { + let deposit = profile_fund_info.deposit; + let new_deposit = deposit.checked_add(&amount_to_fund).expect("Overflow"); + profile_fund_info.deposit = new_deposit; + >::insert( + profile_user_account.clone(), + who.clone(), + profile_fund_info, + ); + } + None => { + let profile_fund_info = ProfileFundInfo { + funder_account_id: who.clone(), + validation_account_id: profile_user_account.clone(), + deposit: amount_to_fund.clone(), + deposit_returned: false, + }; + >::insert( + profile_user_account.clone(), + who.clone(), + profile_fund_info, + ); + } + } + + // Update the total funds collected for the profile. + let next_total_fund = total_funded.checked_add(&amount_to_fund).expect("overflow"); + >::insert( + profile_user_account.clone(), + next_total_fund, + ); + + // Emit a ProfileFund event. + Self::deposit_event(Event::ProfileFund { + profile: profile_user_account, + funder: who, + }); + } else { + // Return an error if the funded amount is greater than required. + Err(Error::::AmountFundedGreaterThanRequired)? + } + + Ok(()) + } + + // Add fees for challenge profile ✔️ + #[pallet::call_index(2)] + #[pallet::weight(0)] + pub fn challenge_profile( + origin: OriginFor, + profile_user_account: T::AccountId, + content: Content, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::ensure_account_id_has_profile(profile_user_account.clone())?; + let now = >::block_number(); + + let fees = Self::profile_registration_challenge_fees(); + + let challenger_fund_info = ChallengerFundInfo { + challengerid: who.clone(), + deposit: fees, + start: now.clone(), + challenge_completed: false, + }; + + let challenger_fund_details = >::get(&profile_user_account); + match challenger_fund_details { + Some(_value) => Err(Error::::ChallengeExits)?, + None => { + let _ = ::Currency::withdraw( + &who, + fees.clone(), + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; + >::insert(&profile_user_account, challenger_fund_info); + } + } + + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::set_to_staking_period_link(key.clone(), phase_data, now)?; + T::SchellingGameSharedSource::create_tree_helper_link(key.clone(), 3)?; + + let count = Self::next_challenge_post_count(); + + let challenge_evidence_post: ChallengeEvidencePost = ChallengeEvidencePost::new( + profile_user_account.clone(), + who.clone(), + content, + None, + ); + + match >::get(&profile_user_account, &who) { + None => { + >::insert(&count, challenge_evidence_post); + NextChallengePostId::::mutate(|n| { + *n += 1; + }); + + >::insert(&profile_user_account, &who, count); + } + Some(_hash) => Err(Error::::PostAlreadyExists)?, + } + Ok(()) + } + + // #[pallet::call_index(2)] + // #[pallet::weight(Weight::from_parts(10_000, u64::MAX) + T::DbWeight::get().reads_writes(2,2))] + // pub fn challenge_evidence( + // origin: OriginFor, + // profile_citizenid: CitizenId, + // content: Content, + // ) -> DispatchResult { + // let who = ensure_signed(origin)?; + // let citizen_account_id = Self::get_citizen_accountid(profile_citizenid)?; + // let count = Self::next_challenge_post_count(); + // let challenge_evidence_post = + // ChallengeEvidencePost::new(citizen_account_id, who.clone(), content, None); + // match >::get(&profile_citizenid, &who) { + // None => { + // >::insert(&count, challenge_evidence_post); + // NextChallengePostId::::mutate(|n| { + // *n += 1; + // }); + + // >::insert(&profile_citizenid, &who, count); + // }, + // Some(_hash) => { + // Err(Error::::PostAlreadyExists)? + // // match >::get(&profile_citizenid) { + // // Some(_challengerfundinfo) => { + // // Err(Error::::ChallengerFundAddedCanNotUpdate)? + // // }, + // // None => { + // // // Update challenger profile + // // >::insert(&count, challenge_evidence_post); + // // let newcount = + // // count.checked_add(1).ok_or(Error::::StorageOverflow)?; + // // >::put(newcount); + // // >::insert(&profile_citizenid, &who, count); + // // }, + // // } + // }, + // } + // Ok(()) + // } + + #[pallet::call_index(3)] + #[pallet::weight(0)] + pub fn challenge_comment_create( + origin: OriginFor, + post_id: ChallengePostId, + content: Content, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let count = Self::next_challenge_post_count(); + let main_evidence_post = Self::challenge_post(post_id).unwrap(); + let challenge_evidence_post = ChallengeEvidencePost::new( + main_evidence_post.kyc_profile_id, + who, + content, + Some(post_id), + ); + + match >::get(&post_id) { + None => Err(Error::::ChallengeDoesNotExists)?, + Some(challenge_evidence_post_c) => { + if challenge_evidence_post_c.is_comment == false { + >::insert(&count, challenge_evidence_post); + NextChallengePostId::::mutate(|n| { + *n += 1; + }); + let mut comment_ids = >::get(&post_id); + match comment_ids.binary_search(&count) { + Ok(_) => Err(Error::::CommentExists)?, + Err(index) => { + comment_ids.insert(index, count.clone()); + >::insert(&post_id, &comment_ids); + } + } + } else { + Err(Error::::IsComment)? + } + } + } + + Ok(()) + } + + // // Does citizen exists ✔️ + // // Has the citizen added profile fund ✔️ + // // Create tree ✔️ + // // Check evidence has been submitted + // #[pallet::call_index(4)] + // #[pallet::weight(Weight::from_parts(10_000, u64::MAX) + T::DbWeight::get().reads_writes(2,2))] + // pub fn challenge_profile( + // origin: OriginFor, + // profile_citizenid: CitizenId, + // ) -> DispatchResult { + // let who = ensure_signed(origin)?; + // let key = SumTreeName::UniqueIdenfier1 { + // citizen_id: profile_citizenid, + // name: "challengeprofile".as_bytes().to_vec(), + // }; + // let phase_data = Self::get_phase_data(); + // let now = >::block_number(); + // let _citizen_account_id = Self::get_citizen_accountid(profile_citizenid)?; + // match >::get(&profile_citizenid) { + // Some(profilefundinfo) => { + // if profilefundinfo.validated == true { + // Err(Error::::ProfileIsAlreadyValidated)?; + // } else { + // let _evidence_stake_block_number = profilefundinfo.start; // remove the profile fund info start + + // T::SchellingGameSharedSource::set_to_staking_period_link( + // key.clone(), + // phase_data, + // now, + // )?; + // } + // }, + // None => { + // Err(Error::::ProfileFundNotExists)?; + // }, + // } + // let deposit = >::get(); + // let imb = ::Currency::withdraw( + // &who, + // deposit, + // WithdrawReasons::TRANSFER, + // ExistenceRequirement::AllowDeath, + // )?; + + // ::Currency::resolve_creating(&Self::fund_profile_account(), imb); + + // match >::get(&profile_citizenid) { + // // 📝 To write update stake for reapply + // Some(_challengerfundinfo) => Err(Error::::ChallengerFundInfoExists)?, + // None => { + // let challenger_fund_info = ChallengerFundInfo { + // challengerid: who, + // deposit, + // start: now, + // challenge_completed: false, + // }; + // >::insert(&profile_citizenid, challenger_fund_info); + // }, + // } + // T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; + + // Ok(()) + // } + + // May be you need to check challeger fund details exists + #[pallet::call_index(5)] + #[pallet::weight(0)] + pub fn pass_period( + origin: OriginFor, + profile_user_account: T::AccountId, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + + let now = >::block_number(); + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; + + Ok(()) + } + + // To Do + // Apply jurors or stake ✔️ + // Update stake + // Draw jurors ✔️ + // Unstaking non selected jurors ✔️ + // Commit vote ✔️ + // Reveal vote ✔️ + // Get winning decision ✔️ + // Incentive distribution ✔️ + + // Staking + // 1. Check for minimum stake ✔️ + // 2. Check period is Staking ✔️ + // 3. Number of people staked + + #[pallet::call_index(6)] + #[pallet::weight(0)] + pub fn apply_jurors( + origin: OriginFor, + profile_user_account: T::AccountId, + stake: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::apply_jurors_helper_link(key, phase_data, who, stake)?; + + Ok(()) + } + + // Draw jurors + // Check period is drawing ✔️ + // Check mininum number of juror staked ✔️ + // Improvements + // Set stake to zero so that they are not drawn again + // Store the drawn juror stake in hashmap storage + // Add min draws along with max draws + #[pallet::call_index(7)] + #[pallet::weight(0)] + pub fn draw_jurors( + origin: OriginFor, + profile_user_account: T::AccountId, + iterations: u64, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; + + Ok(()) + } + + // Unstaking + // Stop drawn juror to unstake ✔️ + #[pallet::call_index(8)] + #[pallet::weight(0)] + pub fn unstaking( + origin: OriginFor, + profile_user_account: T::AccountId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; + Ok(()) + } + + #[pallet::call_index(9)] + #[pallet::weight(0)] + pub fn commit_vote( + origin: OriginFor, + profile_user_account: T::AccountId, + vote_commit: [u8; 32], + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + T::SchellingGameSharedSource::commit_vote_helper_link(key, who, vote_commit)?; + Ok(()) + } + + #[pallet::call_index(10)] + #[pallet::weight(0)] + pub fn reveal_vote( + origin: OriginFor, + profile_user_account: T::AccountId, + choice: u128, + salt: Vec, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + + T::SchellingGameSharedSource::reveal_vote_two_choice_helper_link( + key, who, choice, salt, + )?; + + Ok(()) + } + + #[pallet::call_index(11)] + #[pallet::weight(0)] + pub fn get_incentives( + origin: OriginFor, + profile_user_account: T::AccountId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = >::get(&profile_user_account); + + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + let phase_data = Self::get_phase_data(); + T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( + key, phase_data, who, + )?; + Ok(()) + } + + #[pallet::call_index(12)] + #[pallet::weight(0)] + pub fn return_profile_stake( + origin: OriginFor, + profile_user_account: T::AccountId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = >::get(&profile_user_account); + let key = SumTreeName::ProfileValidation { + citizen_address: profile_user_account.clone(), + block_number, + }; + let now = >::block_number(); + let phase_data = Self::get_phase_data(); + + let period = T::SchellingGameSharedSource::get_period_link(key.clone()).unwrap(); + if period == Period::Execution { + let decision: WinningDecision = + T::SchellingGameSharedSource::get_winning_decision_value_link(key.clone()); + if decision == WinningDecision::WinnerNo { + match >::get(profile_user_account.clone(), who.clone()) { + Some(mut profile_fund_info) => { + if profile_fund_info.deposit_returned == false { + let r = ::Currency::deposit_into_existing( + &who, + profile_fund_info.deposit, + ) + .ok() + .unwrap(); + ::Reward::on_unbalanced(r); + profile_fund_info.deposit_returned = true; + >::insert( + profile_user_account.clone(), + who.clone(), + profile_fund_info, + ); + } else { + Err(Error::::ProfileFundAlreadyReturned)?; + } + } + None => { + Err(Error::::ProfileFundNotExists)?; + } + } + } + } else if period == Period::Evidence { + T::SchellingGameSharedSource::ensure_time_for_staking_over_link( + key, phase_data, now, + )?; + match >::get(profile_user_account.clone(), who.clone()) { + Some(mut profile_fund_info) => { + if profile_fund_info.deposit_returned == false { + let r = ::Currency::deposit_into_existing( + &who, + profile_fund_info.deposit, + ) + .ok() + .unwrap(); + ::Reward::on_unbalanced(r); + profile_fund_info.deposit_returned = true; + >::insert( + profile_user_account.clone(), + who.clone(), + profile_fund_info, + ); + } else { + Err(Error::::ProfileFundAlreadyReturned)?; + } + } + None => { + Err(Error::::ProfileFundNotExists)?; + } + } + } + + Ok(()) + } + } +} diff --git a/custom-pallets/profile-validation/src/mock.rs b/custom-pallets/profile-validation/src/mock.rs new file mode 100644 index 0000000..c97f20d --- /dev/null +++ b/custom-pallets/profile-validation/src/mock.rs @@ -0,0 +1,154 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, parameter_types, + traits::{ConstU16, ConstU64}, +}; +use frame_support_test::TestRandomness; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + ProfileValidation: pallet_template, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + SchellingGameShared: pallet_schelling_game_shared, + SortitionSumGame: pallet_sortition_sum_game, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type AccountData = pallet_balances::AccountData; // New code +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_balances::Config for Test { + type MaxHolds = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; // New code + type SchellingGameSharedSource = SchellingGameShared; + type Slash = (); + type Reward = (); +} + +impl pallet_schelling_game_shared::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; // New code + type RandomnessSource = TestRandomness; + type Slash = (); + type Reward = (); + type SortitionSumGameSource = SortitionSumGame; +} + +impl pallet_sortition_sum_game::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 100000), + (2, 200000), + (3, 300000), + (4, 300000), + (5, 300000), + (6, 300000), + (7, 300000), + (8, 300000), + (9, 300000), + (10, 300000), + (11, 300000), + (12, 300000), + (13, 300000), + (14, 300000), + (15, 300000), + (16, 300000), + (17, 300000), + (18, 300000), + (19, 300000), + (20, 300000), + (21, 300000), + (22, 300000), + (23, 300000), + (24, 300000), + (25, 300000), + (26, 300000), + (27, 300000), + (28, 300000), + (29, 300000), + (30, 300000), + (31, 300000), + (32, 300000), + (33, 300000), + (34, 300000), + (35, 300000), + ], + } // new code + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} diff --git a/custom-pallets/profile-validation/src/permissions.rs b/custom-pallets/profile-validation/src/permissions.rs new file mode 100644 index 0000000..f04e597 --- /dev/null +++ b/custom-pallets/profile-validation/src/permissions.rs @@ -0,0 +1,10 @@ +use crate::*; + +impl Pallet { + pub(super) fn ensure_account_id_has_profile(account_id: T::AccountId) -> DispatchResult { + match >::get(&account_id) { + Some(_) => Ok(()), + None => Err(Error::::CitizenDoNotExists)?, + } + } +} diff --git a/custom-pallets/profile-validation/src/tests.rs b/custom-pallets/profile-validation/src/tests.rs new file mode 100644 index 0000000..f3a8443 --- /dev/null +++ b/custom-pallets/profile-validation/src/tests.rs @@ -0,0 +1,634 @@ +use crate::types::CitizenDetailsPost; +use crate::{mock::*, Error, Event}; +use frame_support::{assert_noop, assert_ok}; +use pallet_schelling_game_shared::types::Period; +use pallet_sortition_sum_game::types::SumTreeName; +use pallet_support::Content; +use pallet_support::WhoAndWhen; + +#[test] +fn add_citizen_profile_check() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + let data = ProfileValidation::citizen_profile(1); + let profile = Some(CitizenDetailsPost:: { + created: WhoAndWhen { + account: 1, + block: 1, + time: 0, + }, + content, + citizen_id: 1, + owner: 1, + edited: false, + hidden: false, + upvotes_count: 0, + downvotes_count: 0, + }); + assert_eq!(data, profile); + System::set_block_number(5); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqz" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + let data = ProfileValidation::citizen_profile(1); + let profile = Some(CitizenDetailsPost:: { + created: WhoAndWhen { + account: 1, + block: 5, + time: 0, + }, + content, + citizen_id: 1, + owner: 1, + edited: false, + hidden: false, + upvotes_count: 0, + downvotes_count: 0, + }); + assert_eq!(data, profile); + }); +} + +#[test] +fn check_fund_addition() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(10); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + let data = ProfileValidation::citizen_profile(1); + let profile = Some(CitizenDetailsPost:: { + created: WhoAndWhen { + account: 1, + block: 10, + time: 0, + }, + content, + citizen_id: 1, + owner: 1, + edited: false, + hidden: false, + upvotes_count: 0, + downvotes_count: 0, + }); + assert_eq!(data, profile); + let balance = Balances::free_balance(3); + assert_eq!(300000, balance); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(3), + 1, + 100 + )); + let balance = Balances::free_balance(3); + assert_eq!(300000 - 100, balance); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqz" + .as_bytes() + .to_vec(), + ); + assert_noop!( + ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone()), + Error::::NoMoreUpdates + ); + let data = ProfileValidation::profile_fund_details(1, 3).unwrap(); + assert_eq!(100, data.deposit); + let total_fund = ProfileValidation::total_fund_for_profile_collected(1); + assert_eq!(100, total_fund); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(3), + 1, + 100 + )); + let balance = Balances::free_balance(3); + assert_eq!(300000 - 200, balance); + let data = ProfileValidation::profile_fund_details(1, 3).unwrap(); + assert_eq!(200, data.deposit); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(4), + 1, + 500 + )); + let balance = Balances::free_balance(4); + assert_eq!(300000 - 500, balance); + let data = ProfileValidation::profile_fund_details(1, 4).unwrap(); + assert_eq!(500, data.deposit); + assert_noop!( + ProfileValidation::add_profile_stake(RuntimeOrigin::signed(5), 1, 1000), + Error::::AmountFundedGreaterThanRequired + ); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(5), + 1, + 300 + )); + System::assert_last_event( + Event::ProfileFund { + profile: 1, + funder: 5, + } + .into(), + ); + + let key = SumTreeName::ProfileValidation { + citizen_address: 1, + block_number: 10, + }; + let period = SchellingGameShared::get_period(key); + assert_eq!(Some(Period::Evidence), period); + }) +} + +#[test] +fn challenge_evidence() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(3), + 1, + 1000 + )); + let key = SumTreeName::ProfileValidation { + citizen_address: 1, + block_number: 1, + }; + let period = SchellingGameShared::get_period(key.clone()); + assert_eq!(Some(Period::Evidence), period); + + let challenge_content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" + .as_bytes() + .to_vec(), + ); + + let phase_data = ProfileValidation::get_phase_data(); + + assert_noop!( + ProfileValidation::challenge_profile( + RuntimeOrigin::signed(4), + 1, + challenge_content.clone() + ), + >::EvidencePeriodNotOver + ); + + System::set_block_number(phase_data.evidence_length + 1); + let fees = ProfileValidation::profile_registration_challenge_fees(); + let balance = Balances::free_balance(4); + assert_eq!(300000, balance); + assert_ok!(ProfileValidation::challenge_profile( + RuntimeOrigin::signed(4), + 1, + challenge_content.clone() + )); + let balance = Balances::free_balance(4); + assert_eq!(300000 - fees, balance); + let period = SchellingGameShared::get_period(key.clone()); + assert_eq!(Some(Period::Staking), period); + + assert_noop!( + ProfileValidation::challenge_profile( + RuntimeOrigin::signed(4), + 2, + challenge_content.clone() + ), + Error::::CitizenDoNotExists + ); + }) +} + +#[test] +fn challenge_profile_after_time_for_staking_over_test() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(3), + 1, + 1000 + )); + let key = SumTreeName::ProfileValidation { + citizen_address: 1, + block_number: 1, + }; + let period = SchellingGameShared::get_period(key.clone()); + assert_eq!(Some(Period::Evidence), period); + + let challenge_content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" + .as_bytes() + .to_vec(), + ); + + let phase_data = ProfileValidation::get_phase_data(); + + assert_noop!( + ProfileValidation::challenge_profile( + RuntimeOrigin::signed(4), + 1, + challenge_content.clone() + ), + >::EvidencePeriodNotOver + ); + + System::set_block_number(phase_data.evidence_length + phase_data.end_of_staking_time + 1); + assert_noop!( + ProfileValidation::challenge_profile( + RuntimeOrigin::signed(4), + 1, + challenge_content.clone() + ), + >::TimeForStakingOver + ); + }); +} + +#[test] +fn return_profile_stake_test() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + let balance = Balances::free_balance(3); + assert_eq!(300000, balance); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(3), + 1, + 400 + )); + let balance = Balances::free_balance(3); + assert_eq!(300000 - 400, balance); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(4), + 1, + 600 + )); + let balance = Balances::free_balance(4); + assert_eq!(300000 - 600, balance); + let key = SumTreeName::ProfileValidation { + citizen_address: 1, + block_number: 1, + }; + let period = SchellingGameShared::get_period(key.clone()); + assert_eq!(Some(Period::Evidence), period); + let phase_data = ProfileValidation::get_phase_data(); + System::set_block_number(phase_data.evidence_length + phase_data.end_of_staking_time); + assert_noop!( + ProfileValidation::return_profile_stake(RuntimeOrigin::signed(3), 1), + >::TimeForStakingNotOver + ); + System::set_block_number(phase_data.evidence_length + phase_data.end_of_staking_time + 1); + assert_ok!(ProfileValidation::return_profile_stake( + RuntimeOrigin::signed(3), + 1 + )); + let balance = Balances::free_balance(3); + assert_eq!(300000, balance); + assert_noop!( + ProfileValidation::return_profile_stake(RuntimeOrigin::signed(3), 1), + Error::::ProfileFundAlreadyReturned + ); + + assert_ok!(ProfileValidation::return_profile_stake( + RuntimeOrigin::signed(4), + 1 + )); + let balance = Balances::free_balance(4); + assert_eq!(300000, balance); + assert_noop!( + ProfileValidation::return_profile_stake(RuntimeOrigin::signed(5), 1), + Error::::ProfileFundNotExists + ); + }); +} + +#[test] +fn schelling_game_test() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(3), + 1, + 1000 + )); + let challenge_content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" + .as_bytes() + .to_vec(), + ); + let phase_data = ProfileValidation::get_phase_data(); + System::set_block_number(phase_data.evidence_length + 1); + assert_ok!(ProfileValidation::challenge_profile( + RuntimeOrigin::signed(4), + 1, + challenge_content.clone() + )); + + let balance = Balances::free_balance(29); + assert_eq!(300000, balance); + for j in 4..30 { + assert_ok!(ProfileValidation::apply_jurors( + RuntimeOrigin::signed(j), + 1, + j * 100 + )); + } + + let balance = Balances::free_balance(29); + assert_eq!(300000 - 29 * 100, balance); + + assert_noop!( + ProfileValidation::draw_jurors(RuntimeOrigin::signed(5), 1, 5), + >::PeriodDontMatch + ); + + assert_noop!( + ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1), + >::StakingPeriodNotOver + ); + + System::set_block_number(phase_data.evidence_length + 1 + phase_data.staking_length); + + assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_ok!(ProfileValidation::draw_jurors( + RuntimeOrigin::signed(5), + 1, + 5 + )); + + let key = SumTreeName::ProfileValidation { + citizen_address: 1, + block_number: 1, + }; + + let draws_in_round = SchellingGameShared::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + + let drawn_jurors = SchellingGameShared::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + + assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); + + let period = SchellingGameShared::get_period(key.clone()); + + assert_eq!(Some(Period::Commit), period); + + let balance: u64 = Balances::free_balance(5); + assert_eq!(300000 - 5 * 100, balance); + assert_ok!(ProfileValidation::unstaking(RuntimeOrigin::signed(5), 1)); + let balance = Balances::free_balance(5); + assert_eq!(300000, balance); + + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_noop!( + ProfileValidation::commit_vote(RuntimeOrigin::signed(6), 1, hash), + >::JurorDoesNotExists + ); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(ProfileValidation::commit_vote( + RuntimeOrigin::signed(4), + 1, + hash + )); + + // You can replace vote within the commit period. + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(ProfileValidation::commit_vote( + RuntimeOrigin::signed(4), + 1, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(ProfileValidation::commit_vote( + RuntimeOrigin::signed(7), + 1, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(ProfileValidation::commit_vote( + RuntimeOrigin::signed(13), + 1, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); + assert_ok!(ProfileValidation::commit_vote( + RuntimeOrigin::signed(14), + 1, + hash + )); + + let hash = sp_io::hashing::keccak_256("0salt6".as_bytes()); + assert_ok!(ProfileValidation::commit_vote( + RuntimeOrigin::signed(15), + 1, + hash + )); + + assert_noop!( + ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1), + >::CommitPeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + 1 + phase_data.staking_length + phase_data.commit_length, + ); + assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_noop!( + ProfileValidation::reveal_vote( + RuntimeOrigin::signed(4), + 1, + 2, + "salt2".as_bytes().to_vec() + ), + >::CommitDoesNotMatch + ); + + assert_ok!(ProfileValidation::reveal_vote( + RuntimeOrigin::signed(4), + 1, + 1, + "salt2".as_bytes().to_vec() + )); + + assert_ok!(ProfileValidation::reveal_vote( + RuntimeOrigin::signed(7), + 1, + 1, + "salt3".as_bytes().to_vec() + )); + + assert_ok!(ProfileValidation::reveal_vote( + RuntimeOrigin::signed(13), + 1, + 1, + "salt4".as_bytes().to_vec() + )); + + assert_ok!(ProfileValidation::reveal_vote( + RuntimeOrigin::signed(14), + 1, + 1, + "salt5".as_bytes().to_vec() + )); + + assert_noop!( + ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1), + >::VotePeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + + 1 + + phase_data.staking_length + + phase_data.commit_length + + phase_data.vote_length, + ); + assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_noop!( + ProfileValidation::get_incentives(RuntimeOrigin::signed(15), 1), + >::VoteNotRevealed + ); + let balance: u64 = Balances::free_balance(14); + assert_eq!(300000 - 14 * 100, balance); + assert_ok!(ProfileValidation::get_incentives( + RuntimeOrigin::signed(14), + 1 + )); + let balance: u64 = Balances::free_balance(14); + assert_eq!(300025, balance); + }) +} + +#[test] +fn test_draw_juror() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProfileValidation::add_citizen( + RuntimeOrigin::signed(1), + content.clone() + )); + + assert_ok!(ProfileValidation::add_profile_stake( + RuntimeOrigin::signed(3), + 1, + 1000 + )); + let challenge_content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" + .as_bytes() + .to_vec(), + ); + let phase_data = ProfileValidation::get_phase_data(); + System::set_block_number(phase_data.evidence_length + 1); + assert_ok!(ProfileValidation::challenge_profile( + RuntimeOrigin::signed(4), + 1, + challenge_content.clone() + )); + + assert_ok!(ProfileValidation::apply_jurors( + RuntimeOrigin::signed(5), + 1, + 100 + )); + assert_ok!(ProfileValidation::apply_jurors( + RuntimeOrigin::signed(6), + 1, + 500 + )); + assert_ok!(ProfileValidation::apply_jurors( + RuntimeOrigin::signed(7), + 1, + 1000 + )); + assert_ok!(ProfileValidation::apply_jurors( + RuntimeOrigin::signed(8), + 1, + 1500 + )); + assert_ok!(ProfileValidation::apply_jurors( + RuntimeOrigin::signed(9), + 1, + 2000 + )); + + System::set_block_number(phase_data.evidence_length + 1 + phase_data.staking_length); + + assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_ok!(ProfileValidation::draw_jurors( + RuntimeOrigin::signed(5), + 1, + 6 + )); + + // assert_ok!(ProfileValidation::draw_jurors(RuntimeOrigin::signed(5), 1, 5)); + }) +} diff --git a/custom-pallets/profile-validation/src/types.rs b/custom-pallets/profile-validation/src/types.rs new file mode 100644 index 0000000..d6cb995 --- /dev/null +++ b/custom-pallets/profile-validation/src/types.rs @@ -0,0 +1,54 @@ +use frame_support::pallet_prelude::*; +use scale_info::TypeInfo; +// use frame_support::sp_std::{vec::Vec}; + +use super::*; + +pub const FIRST_CITIZEN_ID: CitizenId = 1; +pub const FIRST_CHALLENGE_POST_ID: ChallengePostId = 1; + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct CitizenDetailsPost { + pub created: WhoAndWhenOf, + pub content: Content, + pub citizen_id: CitizenId, + pub owner: T::AccountId, + pub edited: bool, + pub hidden: bool, + pub upvotes_count: u32, + pub downvotes_count: u32, +} + +#[derive( + PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, MaxEncodedLen, TypeInfo, +)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct ProfileFundInfo { + pub funder_account_id: AccountId, + pub validation_account_id: AccountId, + pub deposit: Balance, + pub deposit_returned: bool, +} + +#[derive( + PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, MaxEncodedLen, TypeInfo, +)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct ChallengerFundInfo { + pub challengerid: AccountId, + pub deposit: Balance, + pub start: BlockNumber, + pub challenge_completed: bool, +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct ChallengeEvidencePost { + pub created: WhoAndWhenOf, + pub owner: T::AccountId, + pub kyc_profile_id: T::AccountId, + pub content: Content, + pub post_id_if_comment: Option, + pub is_comment: bool, +} diff --git a/pallets/profile-validation/src/weights.rs b/custom-pallets/profile-validation/src/weights.rs similarity index 100% rename from pallets/profile-validation/src/weights.rs rename to custom-pallets/profile-validation/src/weights.rs diff --git a/custom-pallets/project-tips/Cargo.toml b/custom-pallets/project-tips/Cargo.toml new file mode 100644 index 0000000..658adb5 --- /dev/null +++ b/custom-pallets/project-tips/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "pallet-project-tips" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true , optional = true} +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +pallet-timestamp = { workspace = true } +pallet-balances = { workspace = true } +pallet-support = { workspace = true } +pallet-shared-storage = { workspace = true } +trait-shared-storage = { workspace = true } +pallet-schelling-game-shared = { workspace = true } +trait-schelling-game-shared = { workspace = true } +pallet-sortition-sum-game = { workspace = true } + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +frame-support-test = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-std/std", + "pallet-timestamp/std", + "pallet-balances/std", + "pallet-support/std", + "pallet-shared-storage/std", + "pallet-schelling-game-shared/std", + "pallet-sortition-sum-game/std", + "frame-support-test/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/profile-validation/README.md b/custom-pallets/project-tips/README.md similarity index 100% rename from pallets/profile-validation/README.md rename to custom-pallets/project-tips/README.md diff --git a/custom-pallets/project-tips/project-tips-rpc/Cargo.toml b/custom-pallets/project-tips/project-tips-rpc/Cargo.toml new file mode 100644 index 0000000..026c604 --- /dev/null +++ b/custom-pallets/project-tips/project-tips-rpc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "project-tips-rpc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +jsonrpsee = { workspace = true } +sc-rpc = { workspace = true } +sp-api = { workspace = true } +sp-blockchain = { workspace = true } +sp-runtime = { workspace = true } +project-tips-runtime-api = { workspace = true } \ No newline at end of file diff --git a/pallets/project-tips/project-tips-rpc/src/lib.rs b/custom-pallets/project-tips/project-tips-rpc/src/lib.rs similarity index 100% rename from pallets/project-tips/project-tips-rpc/src/lib.rs rename to custom-pallets/project-tips/project-tips-rpc/src/lib.rs diff --git a/custom-pallets/project-tips/project-tips-runtime-api/Cargo.toml b/custom-pallets/project-tips/project-tips-runtime-api/Cargo.toml new file mode 100644 index 0000000..912e78d --- /dev/null +++ b/custom-pallets/project-tips/project-tips-runtime-api/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "project-tips-runtime-api" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[dependencies] +parity-scale-codec = { workspace = true } +sp-api = { workspace = true } +frame-support = { workspace = true } +sp-std = { workspace = true} + +[features] +default = ["std"] +std = [ + "sp-api/std", + "frame-support/std", + "sp-std/std", +] \ No newline at end of file diff --git a/custom-pallets/project-tips/project-tips-runtime-api/src/lib.rs b/custom-pallets/project-tips/project-tips-runtime-api/src/lib.rs new file mode 100644 index 0000000..b2478dd --- /dev/null +++ b/custom-pallets/project-tips/project-tips-runtime-api/src/lib.rs @@ -0,0 +1,20 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// use frame_support::sp_std::{vec::Vec}; +// or +use parity_scale_codec::Codec; +use sp_std::prelude::*; + +type ProjectId = u64; + +sp_api::decl_runtime_apis! { + pub trait ProjectTipsApi where AccountId: Codec{ + + fn get_evidence_period_end_block(project_id: ProjectId) -> Option; + fn get_staking_period_end_block(project_id: ProjectId) -> Option; + fn get_drawing_period_end(project_id: ProjectId) -> (u64, u64, bool); + fn get_commit_period_end_block(project_id: ProjectId) -> Option; + fn get_vote_period_end_block(project_id: ProjectId) -> Option; + fn selected_as_juror(project_id: ProjectId, who: AccountId) -> bool; + } +} diff --git a/custom-pallets/project-tips/src/benchmarking.rs b/custom-pallets/project-tips/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/project-tips/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/project-tips/src/extras.rs b/custom-pallets/project-tips/src/extras.rs new file mode 100644 index 0000000..0546b0d --- /dev/null +++ b/custom-pallets/project-tips/src/extras.rs @@ -0,0 +1,210 @@ +use crate::*; + +impl Project { + pub fn new( + project_id: ProjectId, + department_id: DepartmentId, + content: Content, + tipping_name: TippingName, + funding_needed: BalanceOf, + project_leader: T::AccountId, + ) -> Self { + Project { + created: new_who_and_when::(project_leader.clone()), + project_id, + department_id, + content, + tipping_name, + funding_needed, + project_leader, + } + } +} + +impl Incentives { + pub fn new(number_of_games: u64, winner: u64, loser: u64, stake: u64) -> Self { + Incentives { + number_of_games: number_of_games, + winner: winner, + loser: loser, + total_stake: stake, + start: new_when_details::(), + } + } +} + +impl Pallet { + pub(super) fn get_phase_data() -> PhaseData { + T::SchellingGameSharedSource::create_phase_data(50, 5, 3, 100, (100, 100)) + } + + pub fn ensure_user_is_project_creator_and_project_exists( + project_id: ProjectId, + user: T::AccountId, + ) -> DispatchResult { + let project_option: Option> = >::get(project_id); + match project_option { + Some(project) => { + let project_leader = project.project_leader; + ensure!(project_leader == user, Error::::ProjectCreatorDontMatch); + } + None => Err(Error::::ProjectDontExists)?, + } + + Ok(()) + } + + pub fn ensure_staking_period_set_once_project_id(project_id: ProjectId) -> DispatchResult { + let block_number_option = >::get(project_id); + match block_number_option { + Some(_block) => Err(Error::::ProjectIdStakingPeriodAlreadySet)?, + None => Ok(()), + } + } + + pub fn get_block_number_of_schelling_game( + project_id: ProjectId, + ) -> Result, DispatchError> { + let block_number_option = >::get(project_id); + let block_number = match block_number_option { + Some(block_number) => block_number, + None => Err(Error::::BlockNumberProjectIdNotExists)?, + }; + Ok(block_number) + } + + pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { + input.saturated_into::>() + } + + pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { + input.saturated_into::>() + } + + pub fn value_of_tipping_name(tipping: TippingName) -> TippingValue> { + match tipping { + TippingName::SmallTipper => TippingValue { + max_tipping_value: 10_000u64.saturated_into::>(), + stake_required: 10u64.saturated_into::>(), + }, + TippingName::BigTipper => TippingValue { + max_tipping_value: 100_000u64.saturated_into::>(), + stake_required: 50u64.saturated_into::>(), + }, + TippingName::SmallSpender => TippingValue { + max_tipping_value: 1_000_000u64.saturated_into::>(), + stake_required: 100u64.saturated_into::>(), + }, + TippingName::MediumSpender => TippingValue { + max_tipping_value: 10_000_000u64.saturated_into::>(), + stake_required: 200u64.saturated_into::>(), + }, + TippingName::BigSpender => TippingValue { + max_tipping_value: 100_000_000u64.saturated_into::>(), + stake_required: 500u64.saturated_into::>(), + }, + } + } + + // Block code start + + pub fn get_evidence_period_end_block(project_id: ProjectId) -> Option { + let now = >::block_number(); + + let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_staking_period_end_block(project_id: ProjectId) -> Option { + let now = >::block_number(); + + let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_drawing_period_end(project_id: ProjectId) -> (u64, u64, bool) { + let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + let phase_data = Self::get_phase_data(); + + let result = + T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); + result + } + + pub fn get_commit_period_end_block(project_id: ProjectId) -> Option { + let now = >::block_number(); + + let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn get_vote_period_end_block(project_id: ProjectId) -> Option { + let now = >::block_number(); + + let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( + key, phase_data, now, + ); + result + } + + pub fn selected_as_juror(project_id: ProjectId, who: T::AccountId) -> bool { + let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); + result + } + + // Block code end +} diff --git a/custom-pallets/project-tips/src/lib.rs b/custom-pallets/project-tips/src/lib.rs new file mode 100644 index 0000000..2a42f8a --- /dev/null +++ b/custom-pallets/project-tips/src/lib.rs @@ -0,0 +1,556 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +// One can enhance validation measures by increasing staking power for local residents or individuals with positive externalities—those who contribute to the network for a good cause. +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +mod extras; +mod types; + +use frame_support::pallet_prelude::DispatchError; +use frame_support::pallet_prelude::*; +use frame_support::sp_runtime::traits::Saturating; +use frame_support::sp_runtime::SaturatedConversion; +use frame_support::{dispatch::DispatchResult, ensure}; +use frame_system::pallet_prelude::*; +use sp_std::prelude::*; + +use frame_support::{ + traits::{ + Currency, ExistenceRequirement, Get, OnUnbalanced, ReservableCurrency, WithdrawReasons, + }, + PalletId, +}; +use pallet_schelling_game_shared::types::{ + JurorGameResult, Period, PhaseData, RangePoint, SchellingGameType, +}; +use pallet_sortition_sum_game::types::SumTreeName; +use pallet_support::{ + ensure_content_is_valid, new_when_details, new_who_and_when, remove_from_vec, Content, + WhenDetails, WhenDetailsOf, WhoAndWhen, WhoAndWhenOf, +}; +use trait_schelling_game_shared::SchellingGameSharedLink; +use trait_shared_storage::SharedStorageLink; +pub use types::PROJECT_ID; +use types::{Incentives, IncentivesMetaData, Project, TippingName, TippingValue}; + +type AccountIdOf = ::AccountId; +type BalanceOf = <::Currency as Currency>>::Balance; +type PositiveImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::PositiveImbalance; +pub type BlockNumberOf = BlockNumberFor; +pub type SumTreeNameType = SumTreeName, BlockNumberOf>; +type DepartmentId = u64; +type ProjectId = u64; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_schelling_game_shared::Config + pallet_timestamp::Config + { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + + type SharedStorageSource: SharedStorageLink>; + type SchellingGameSharedSource: SchellingGameSharedLink< + SumTreeName = SumTreeName>, + SchellingGameType = SchellingGameType, + BlockNumber = BlockNumberOf, + AccountId = AccountIdOf, + Balance = BalanceOf, + RangePoint = RangePoint, + Period = Period, + PhaseData = PhaseData, + JurorGameResult = JurorGameResult, + >; + type Currency: ReservableCurrency; + + /// Handler for the unbalanced increment when rewarding (minting rewards) + type Reward: OnUnbalanced>; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + #[pallet::type_value] + pub fn MinimumDepartmentStake() -> BalanceOf { + 10000u128.saturated_into::>() + } + + #[pallet::type_value] + pub fn DefaultForNextProjectId() -> ProjectId { + PROJECT_ID + } + + #[pallet::storage] + #[pallet::getter(fn next_project_id)] + pub type NextProjectId = + StorageValue<_, ProjectId, ValueQuery, DefaultForNextProjectId>; + + #[pallet::storage] + #[pallet::getter(fn get_project)] + pub type Projects = StorageMap<_, Blake2_128Concat, ProjectId, Project>; + + #[pallet::storage] + #[pallet::getter(fn get_projects_from_accounts)] + pub type AccountProjects = + StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + + // #[pallet::storage] + // #[pallet::getter(fn department_stake)] + // pub type DepartmentStakeBalance = + // StorageMap<_, Twox64Concat, DepartmentId, BalanceOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn validation_block)] + pub type ValidationBlock = + StorageMap<_, Blake2_128Concat, ProjectId, BlockNumberOf>; + + #[pallet::storage] + #[pallet::getter(fn incentives_count)] + pub type IncentiveCount = + StorageMap<_, Blake2_128Concat, T::AccountId, Incentives>; + + #[pallet::type_value] + pub fn IncentivesMetaValue() -> IncentivesMetaData { + IncentivesMetaData::default() + } + + #[pallet::storage] + #[pallet::getter(fn incentives_meta)] + pub type IncentivesMeta = + StorageValue<_, IncentivesMetaData, ValueQuery, IncentivesMetaValue>; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + ProjectCreated { + account: T::AccountId, + project_id: ProjectId, + }, + StakinPeriodStarted { + project_id: ProjectId, + block_number: BlockNumberOf, + }, + ApplyJurors { + project_id: ProjectId, + block_number: BlockNumberOf, + account: T::AccountId, + }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + LessThanMinStake, + CannotStakeNow, + ChoiceOutOfRange, + FundingMoreThanTippingValue, + ProjectDontExists, + ProjectCreatorDontMatch, + ProjectIdStakingPeriodAlreadySet, + BlockNumberProjectIdNotExists, + NotReachedMinimumDecision, + NoIncentiveCount, + } + + // Check deparment exists, it will done using loose coupling + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn create_project( + origin: OriginFor, + department_id: DepartmentId, + content: Content, + tipping_name: TippingName, + funding_needed: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let new_project_id = Self::next_project_id(); + let tipping_value = Self::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + ensure!( + funding_needed <= max_tipping_value, + Error::::FundingMoreThanTippingValue + ); + let new_project: Project = Project::new( + new_project_id, + department_id, + content, + tipping_name, + funding_needed, + who.clone(), + ); + + Projects::insert(new_project_id, new_project); + NextProjectId::::mutate(|n| { + *n += 1; + }); + + AccountProjects::::mutate(&who, |projects| { + let idx = projects + .binary_search(&new_project_id) + .unwrap_or_else(|x| x); + projects.insert(idx, new_project_id); + }); + + Self::deposit_event(Event::ProjectCreated { + account: who, + project_id: new_project_id, + }); + + Ok(()) + } + + // Check update and discussion time over, only project creator can apply staking period + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn apply_staking_period(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::ensure_user_is_project_creator_and_project_exists(project_id, who.clone())?; + Self::ensure_staking_period_set_once_project_id(project_id)?; + match >::get(project_id) { + Some(project) => { + let tipping_name = project.tipping_name; + let tipping_value = Self::value_of_tipping_name(tipping_name); + let stake_required = tipping_value.stake_required; + + let _ = ::Currency::withdraw( + &who, + stake_required, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; + } + + None => Err(Error::::ProjectDontExists)?, + } + + let now = >::block_number(); + + let key = SumTreeName::ProjectTips { + project_id, + block_number: now.clone(), + }; + + >::insert(project_id, now.clone()); + // check what if called again, its done with `ensure_staking_period_set_once_project_id` + T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now.clone())?; + T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; + + Self::deposit_event(Event::StakinPeriodStarted { + project_id, + block_number: now, + }); + + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(0)] + pub fn apply_jurors( + origin: OriginFor, + project_id: ProjectId, + stake: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let block_number = Self::get_block_number_of_schelling_game(project_id)?; + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::apply_jurors_helper_link( + key, + phase_data, + who.clone(), + stake, + )?; + Self::deposit_event(Event::ApplyJurors { + project_id, + block_number, + account: who, + }); + + Ok(()) + } + + #[pallet::call_index(3)] + #[pallet::weight(0)] + pub fn pass_period(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let block_number = Self::get_block_number_of_schelling_game(project_id)?; + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let now = >::block_number(); + let phase_data = Self::get_phase_data(); + T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; + Ok(()) + } + + #[pallet::call_index(4)] + #[pallet::weight(0)] + pub fn draw_jurors( + origin: OriginFor, + project_id: ProjectId, + iterations: u64, + ) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let block_number = Self::get_block_number_of_schelling_game(project_id)?; + + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + let phase_data = Self::get_phase_data(); + + T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; + + Ok(()) + } + + // Unstaking + // Stop drawn juror to unstake ✔️ + #[pallet::call_index(5)] + #[pallet::weight(0)] + pub fn unstaking(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = Self::get_block_number_of_schelling_game(project_id)?; + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; + Ok(()) + } + + #[pallet::call_index(6)] + #[pallet::weight(0)] + pub fn commit_vote( + origin: OriginFor, + project_id: ProjectId, + vote_commit: [u8; 32], + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = Self::get_block_number_of_schelling_game(project_id)?; + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + T::SchellingGameSharedSource::commit_vote_helper_link(key, who, vote_commit)?; + Ok(()) + } + + #[pallet::call_index(7)] + #[pallet::weight(0)] + pub fn reveal_vote( + origin: OriginFor, + project_id: ProjectId, + choice: u128, + salt: Vec, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let block_number = Self::get_block_number_of_schelling_game(project_id)?; + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + + T::SchellingGameSharedSource::reveal_vote_two_choice_helper_link( + key, who, choice, salt, + )?; + Ok(()) + } + + #[pallet::call_index(8)] + #[pallet::weight(0)] + pub fn add_incentive_count(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let who = ensure_signed(origin)?; + let block_number = Self::get_block_number_of_schelling_game(project_id)?; + let key = SumTreeName::ProjectTips { + project_id, + block_number: block_number.clone(), + }; + let (juror_game_result, stake) = + T::SchellingGameSharedSource::get_result_of_juror(key.clone(), who.clone())?; + + T::SchellingGameSharedSource::add_to_incentives_count(key, who.clone())?; + let incentive_count_option = >::get(&who); + match incentive_count_option { + Some(mut incentive) => { + match juror_game_result { + JurorGameResult::Won => { + incentive.number_of_games += 1; + incentive.winner += 1; + incentive.total_stake += stake; + } + JurorGameResult::Lost => { + incentive.number_of_games += 1; + incentive.loser += 1; + incentive.total_stake += stake; + } + + JurorGameResult::Draw => { + incentive.number_of_games += 1; + incentive.total_stake += stake; + } + }; + >::mutate(&who, |incentive_option| { + *incentive_option = Some(incentive); + }); + } + None => { + let mut winner = 0; + let mut loser = 0; + match juror_game_result { + JurorGameResult::Won => { + winner = 1; + } + JurorGameResult::Lost => { + loser = 1; + } + JurorGameResult::Draw => {} + }; + let number_of_games = 1; + let new_incentives: Incentives = + Incentives::new(number_of_games, winner, loser, stake); + >::insert(&who, new_incentives); + } + } + + Ok(()) + } + + // Provide incentives + + #[pallet::call_index(9)] + #[pallet::weight(0)] + pub fn get_incentives(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let incentive_meta = >::get(); + let total_games_allowed = incentive_meta.total_number; + let incentive_count_option = >::get(&who); + match incentive_count_option { + Some(incentive) => { + let total_number_games = incentive.number_of_games; + if total_number_games >= total_games_allowed { + let new_incentives: Incentives = Incentives::new(0, 0, 0, 0); + >::mutate(&who, |incentive_option| { + *incentive_option = Some(new_incentives); + }); + + let total_win = incentive.winner; + let total_lost = incentive.loser; + + // Define multipliers + let win_multiplier = 10 * 100; + let lost_multiplier = incentive_meta.disincentive_times * 100; + + // Calculate total_win_incentives and total_lost_incentives + let total_win_incentives = total_win.checked_mul(win_multiplier); + let total_lost_incentives = total_lost.checked_mul(lost_multiplier); + + // Calculate total_incentives, handling overflow or negative errors + let total_incentives = match (total_win_incentives, total_lost_incentives) { + (Some(win), Some(lost)) => win.checked_sub(lost).unwrap_or(0), + _ => 0, // If multiplication overflowed, set total_incentives to 0 + }; + + let mut stake = incentive.total_stake; + // Deduct 1% of the stake if total_lost > total_win + if total_lost > total_win { + let stake_deduction = stake / 100; // 1% of the stake + stake = stake.checked_sub(stake_deduction).unwrap_or(stake); + // Safe subtraction + // println!("Stake deducted by 1%: {}", stake); + } + + let total_fund = stake.checked_add(total_incentives).unwrap_or(0); + + let balance = Self::u64_to_balance_saturated(total_fund); + + let r = + ::Currency::deposit_into_existing(&who, balance) + .ok() + .unwrap(); + ::Reward::on_unbalanced(r); + // Provide the incentives + } else { + Err(Error::::NotReachedMinimumDecision)? + } + } + None => Err(Error::::NoIncentiveCount)?, + } + Ok(()) + } + + // #[pallet::call_index(8)] + // #[pallet::weight(0)] + // pub fn get_incentives(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + // let who = ensure_signed(origin)?; + // let block_number = Self::get_block_number_of_schelling_game(project_id)?; + // let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; + + // let phase_data = Self::get_phase_data(); + // T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( + // key, phase_data, who, + // )?; + // Ok(()) + // } + } +} diff --git a/custom-pallets/project-tips/src/mock.rs b/custom-pallets/project-tips/src/mock.rs new file mode 100644 index 0000000..a1b452a --- /dev/null +++ b/custom-pallets/project-tips/src/mock.rs @@ -0,0 +1,164 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, parameter_types, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; +use frame_support_test::TestRandomness; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + ProjectTips: pallet_template, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + SharedStorage:pallet_shared_storage, + SchellingGameShared: pallet_schelling_game_shared, + SortitionSumGame: pallet_sortition_sum_game, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type AccountData = pallet_balances::AccountData; // New code +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_shared_storage::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type SharedStorageSource = SharedStorage; + type Currency = Balances; // New code + type SchellingGameSharedSource = SchellingGameShared; + type Reward = (); +} + +impl pallet_balances::Config for Test { + type MaxHolds = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +impl pallet_schelling_game_shared::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; // New code + type RandomnessSource = TestRandomness; + type Slash = (); + type Reward = (); + type SortitionSumGameSource = SortitionSumGame; +} + +impl pallet_sortition_sum_game::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 1000000), + (2, 2000000), + (3, 3000000), + (4, 3000000), + (5, 3000000), + (6, 3000000), + (7, 3000000), + (8, 3000000), + (9, 3000000), + (10, 3000000), + (11, 3000000), + (12, 3000000), + (13, 3000000), + (14, 3000000), + (15, 3000000), + (16, 3000000), + (17, 3000000), + (18, 3000000), + (19, 3000000), + (20, 3000000), + (21, 3000000), + (22, 3000000), + (23, 3000000), + (24, 3000000), + (25, 3000000), + (26, 3000000), + (27, 3000000), + (28, 3000000), + (29, 3000000), + (30, 3000000), + (31, 3000000), + (32, 3000000), + (33, 3000000), + (34, 3000000), + (35, 3000000), + ], + } // new code + .assimilate_storage(&mut t) + .unwrap(); + pallet_shared_storage::GenesisConfig:: { + approved_citizen_address: vec![1, 2], + } + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} diff --git a/custom-pallets/project-tips/src/tests.rs b/custom-pallets/project-tips/src/tests.rs new file mode 100644 index 0000000..a9a17ac --- /dev/null +++ b/custom-pallets/project-tips/src/tests.rs @@ -0,0 +1,1153 @@ +use crate::types::{Incentives, TippingName}; +use crate::{mock::*, Error, Event}; +use frame_support::{assert_noop, assert_ok}; +use pallet_schelling_game_shared::types::Period; +use pallet_sortition_sum_game::types::SumTreeName; +use pallet_support::Content; +use pallet_support::WhenDetails; + +#[test] +fn check_balance_on_staking() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + let tipping_name = TippingName::SmallTipper; + let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let stake_required = tipping_value.stake_required; + let funding_needed = max_tipping_value - 100; + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProjectTips::create_project( + RuntimeOrigin::signed(1), + 2, + content.clone(), + tipping_name, + funding_needed + )); + + System::assert_last_event( + Event::ProjectCreated { + account: 1, + project_id: 1, + } + .into(), + ); + + let balance = Balances::free_balance(1); + + assert_ok!(ProjectTips::apply_staking_period( + RuntimeOrigin::signed(1), + 1 + )); + + let after_balance = Balances::free_balance(1); + + assert_eq!(after_balance, balance - stake_required); + }); +} + +#[test] +fn check_create_project_function() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let tipping_name = TippingName::SmallTipper; + let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let funding_needed = max_tipping_value - 100; + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProjectTips::create_project( + RuntimeOrigin::signed(1), + 2, + content.clone(), + tipping_name, + funding_needed + )); + + System::assert_last_event( + Event::ProjectCreated { + account: 1, + project_id: 1, + } + .into(), + ); + + let next_project_id = ProjectTips::next_project_id(); + + assert_eq!(2, next_project_id); + + let funding_needed = max_tipping_value + 100; + + assert_noop!( + ProjectTips::create_project( + RuntimeOrigin::signed(1), + 2, + content, + tipping_name, + funding_needed + ), + Error::::FundingMoreThanTippingValue + ); + }); +} + +#[test] +fn check_apply_staking_period_function() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert_noop!( + ProjectTips::apply_staking_period(RuntimeOrigin::signed(1), 2), + Error::::ProjectDontExists + ); + + let tipping_name = TippingName::SmallTipper; + let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let funding_needed = max_tipping_value - 100; + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProjectTips::create_project( + RuntimeOrigin::signed(1), + 2, + content, + tipping_name, + funding_needed + )); + + assert_noop!( + ProjectTips::apply_staking_period(RuntimeOrigin::signed(3), 1), + Error::::ProjectCreatorDontMatch + ); + + assert_ok!(ProjectTips::apply_staking_period( + RuntimeOrigin::signed(1), + 1 + )); + + System::assert_last_event( + Event::StakinPeriodStarted { + project_id: 1, + block_number: 1, + } + .into(), + ); + System::set_block_number(5); + assert_noop!( + ProjectTips::apply_staking_period(RuntimeOrigin::signed(1), 1), + Error::::ProjectIdStakingPeriodAlreadySet + ); + }); +} + +#[test] +fn schelling_game_test() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let tipping_name = TippingName::SmallTipper; + let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let stake_required = tipping_value.stake_required; + let funding_needed = max_tipping_value - 100; + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProjectTips::create_project( + RuntimeOrigin::signed(1), + 2, + content, + tipping_name, + funding_needed + )); + + let balance = Balances::free_balance(1); + + assert_ok!(ProjectTips::apply_staking_period( + RuntimeOrigin::signed(1), + 1 + )); + + let after_balance = Balances::free_balance(1); + + assert_eq!(after_balance, balance - stake_required); + + let phase_data = ProjectTips::get_phase_data(); + + let balance = Balances::free_balance(29); + assert_eq!(3000000, balance); + for j in 4..30 { + assert_ok!(ProjectTips::apply_jurors( + RuntimeOrigin::signed(j), + 1, + j * 100 + )); + } + + let balance = Balances::free_balance(29); + assert_eq!(3000000 - 29 * 100, balance); + + assert_noop!( + ProjectTips::draw_jurors(RuntimeOrigin::signed(5), 1, 5), + >::PeriodDontMatch + ); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), + >::StakingPeriodNotOver + ); + + System::set_block_number(1 + phase_data.staking_length); + + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_ok!(ProjectTips::draw_jurors(RuntimeOrigin::signed(5), 1, 5)); + + let key = SumTreeName::ProjectTips { + project_id: 1, + block_number: 1, + }; + + let draws_in_round = SchellingGameShared::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + + let drawn_jurors = SchellingGameShared::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + let period = SchellingGameShared::get_period(key.clone()); + + assert_eq!(Some(Period::Commit), period); + + let balance: u64 = Balances::free_balance(5); + assert_eq!(3000000 - 5 * 100, balance); + assert_ok!(ProjectTips::unstaking(RuntimeOrigin::signed(5), 1)); + let balance = Balances::free_balance(5); + assert_eq!(3000000, balance); + + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_noop!( + ProjectTips::commit_vote(RuntimeOrigin::signed(6), 1, hash), + >::JurorDoesNotExists + ); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(4), 1, hash)); + + // You can replace vote within the commit period. + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(4), 1, hash)); + + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(7), 1, hash)); + + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(13), 1, hash)); + + let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(14), 1, hash)); + + let hash = sp_io::hashing::keccak_256("0salt6".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(15), 1, hash)); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), + >::CommitPeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + 1 + phase_data.staking_length + phase_data.commit_length, + ); + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_noop!( + ProjectTips::reveal_vote(RuntimeOrigin::signed(4), 1, 2, "salt2".as_bytes().to_vec()), + >::CommitDoesNotMatch + ); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(4), + 1, + 1, + "salt2".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(7), + 1, + 1, + "salt3".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(13), + 1, + 1, + "salt4".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(14), + 1, + 1, + "salt5".as_bytes().to_vec() + )); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), + >::VotePeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + + 1 + + phase_data.staking_length + + phase_data.commit_length + + phase_data.vote_length, + ); + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + // assert_noop!( + // ProjectTips::get_incentives(RuntimeOrigin::signed(15), 1), + // >::VoteNotRevealed + // ); + // let balance: u64 = Balances::free_balance(14); + // assert_eq!(3000000 - 14 * 100, balance); + // assert_ok!(ProjectTips::get_incentives(RuntimeOrigin::signed(14), 1)); + // let balance: u64 = Balances::free_balance(14); + // assert_eq!(300025, balance); + }) +} + +#[test] +fn schelling_game_incentives_test() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let tipping_name = TippingName::SmallTipper; + let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let stake_required = tipping_value.stake_required; + let funding_needed = max_tipping_value - 100; + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProjectTips::create_project( + RuntimeOrigin::signed(1), + 2, + content, + tipping_name, + funding_needed + )); + + let balance = Balances::free_balance(1); + + assert_ok!(ProjectTips::apply_staking_period( + RuntimeOrigin::signed(1), + 1 + )); + + let after_balance = Balances::free_balance(1); + + assert_eq!(after_balance, balance - stake_required); + + let phase_data = ProjectTips::get_phase_data(); + + let balance = Balances::free_balance(29); + assert_eq!(3000000, balance); + for j in 4..30 { + assert_ok!(ProjectTips::apply_jurors( + RuntimeOrigin::signed(j), + 1, + j * 100 + )); + } + + let balance = Balances::free_balance(29); + assert_eq!(3000000 - 29 * 100, balance); + + assert_noop!( + ProjectTips::draw_jurors(RuntimeOrigin::signed(5), 1, 5), + >::PeriodDontMatch + ); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), + >::StakingPeriodNotOver + ); + + System::set_block_number(1 + phase_data.staking_length); + + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_ok!(ProjectTips::draw_jurors(RuntimeOrigin::signed(5), 1, 5)); + + let key = SumTreeName::ProjectTips { + project_id: 1, + block_number: 1, + }; + + let draws_in_round = SchellingGameShared::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + + let drawn_jurors = SchellingGameShared::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + let period = SchellingGameShared::get_period(key.clone()); + + assert_eq!(Some(Period::Commit), period); + + let balance: u64 = Balances::free_balance(5); + assert_eq!(3000000 - 5 * 100, balance); + assert_ok!(ProjectTips::unstaking(RuntimeOrigin::signed(5), 1)); + let balance = Balances::free_balance(5); + assert_eq!(3000000, balance); + + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_noop!( + ProjectTips::commit_vote(RuntimeOrigin::signed(6), 1, hash), + >::JurorDoesNotExists + ); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(4), 1, hash)); + + // You can replace vote within the commit period. + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(4), 1, hash)); + + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(7), 1, hash)); + + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(13), 1, hash)); + + let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(14), 1, hash)); + + let hash = sp_io::hashing::keccak_256("0salt6".as_bytes()); + assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(15), 1, hash)); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), + >::CommitPeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + 1 + phase_data.staking_length + phase_data.commit_length, + ); + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_noop!( + ProjectTips::reveal_vote(RuntimeOrigin::signed(4), 1, 2, "salt2".as_bytes().to_vec()), + >::CommitDoesNotMatch + ); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(4), + 1, + 1, + "salt2".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(7), + 1, + 1, + "salt3".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(14), + 1, + 1, + "salt5".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(15), + 1, + 0, + "salt6".as_bytes().to_vec() + )); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), + >::VotePeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + + 1 + + phase_data.staking_length + + phase_data.commit_length + + phase_data.vote_length, + ); + assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); + + assert_noop!( + ProjectTips::add_incentive_count(RuntimeOrigin::signed(13), 1), + >::VoteNotRevealed + ); + + assert_ok!(ProjectTips::add_incentive_count( + RuntimeOrigin::signed(14), + 1 + )); + + let incentive_count = ProjectTips::incentives_count(14).unwrap(); + // println!("{:?}", incentive_count); + // Explicitly specify the type of `incentive_count_eq` + let incentive_count_eq: Incentives = Incentives { + number_of_games: 1, + winner: 1, + loser: 0, + total_stake: 14 * 100, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + // Your test logic here + assert_eq!(incentive_count, incentive_count_eq); + + assert_noop!( + ProjectTips::add_incentive_count(RuntimeOrigin::signed(14), 1), + >::AlreadyIncentivesAdded + ); + + assert_ok!(ProjectTips::add_incentive_count( + RuntimeOrigin::signed(15), + 1 + )); + + let incentive_count = ProjectTips::incentives_count(15).unwrap(); + + let incentive_count_eq: Incentives = Incentives { + number_of_games: 1, + winner: 0, + loser: 1, + total_stake: 15 * 100, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + assert_eq!(incentive_count, incentive_count_eq); + }) +} + +// Play two schelling game to check incentives are updated + +fn full_schelling_game_func(who_ask_tipper: u64, start_block_number: u64) { + let tipping_name = TippingName::SmallTipper; + let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let stake_required = tipping_value.stake_required; + let funding_needed = max_tipping_value - 100; + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProjectTips::create_project( + RuntimeOrigin::signed(who_ask_tipper), + 2, + content, + tipping_name, + funding_needed + )); + + let project_ids = ProjectTips::get_projects_from_accounts(who_ask_tipper); + let project_id = project_ids.last().unwrap(); + let project_id = *project_id; + + let balance = Balances::free_balance(who_ask_tipper); + + assert_ok!(ProjectTips::apply_staking_period( + RuntimeOrigin::signed(who_ask_tipper), + project_id + )); + + let after_balance = Balances::free_balance(who_ask_tipper); + + assert_eq!(after_balance, balance - stake_required); + + let phase_data = ProjectTips::get_phase_data(); + + for j in 4..30 { + assert_ok!(ProjectTips::apply_jurors( + RuntimeOrigin::signed(j), + project_id, + j * 100 + )); + } + + assert_noop!( + ProjectTips::draw_jurors(RuntimeOrigin::signed(5), project_id, 5), + >::PeriodDontMatch + ); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), project_id), + >::StakingPeriodNotOver + ); + + System::set_block_number(start_block_number + phase_data.staking_length); + + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + assert_ok!(ProjectTips::draw_jurors( + RuntimeOrigin::signed(5), + project_id, + 5 + )); + + let key = SumTreeName::ProjectTips { + project_id: project_id, + block_number: start_block_number, + }; + + let draws_in_round = SchellingGameShared::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + + let drawn_jurors = SchellingGameShared::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + let period = SchellingGameShared::get_period(key.clone()); + + assert_eq!(Some(Period::Commit), period); + + assert_ok!(ProjectTips::unstaking(RuntimeOrigin::signed(5), project_id)); + + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_noop!( + ProjectTips::commit_vote(RuntimeOrigin::signed(6), project_id, hash), + >::JurorDoesNotExists + ); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(4), + project_id, + hash + )); + + // You can replace vote within the commit period. + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(4), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(7), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(13), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(14), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("0salt6".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(15), + project_id, + hash + )); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), project_id), + >::CommitPeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + + start_block_number + + phase_data.staking_length + + phase_data.commit_length, + ); + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + assert_noop!( + ProjectTips::reveal_vote( + RuntimeOrigin::signed(4), + project_id, + 2, + "salt2".as_bytes().to_vec() + ), + >::CommitDoesNotMatch + ); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(4), + project_id, + 1, + "salt2".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(7), + project_id, + 1, + "salt3".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(14), + project_id, + 1, + "salt5".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(15), + project_id, + 0, + "salt6".as_bytes().to_vec() + )); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), project_id), + >::VotePeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + + start_block_number + + phase_data.staking_length + + phase_data.commit_length + + phase_data.vote_length, + ); + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + assert_noop!( + ProjectTips::add_incentive_count(RuntimeOrigin::signed(13), project_id), + >::VoteNotRevealed + ); + assert_ok!(ProjectTips::add_incentive_count( + RuntimeOrigin::signed(14), + project_id + )); + assert_ok!(ProjectTips::add_incentive_count( + RuntimeOrigin::signed(15), + project_id + )); +} + +fn full_schelling_game_func2(who_ask_tipper: u64, start_block_number: u64) { + let tipping_name = TippingName::SmallTipper; + let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); + let max_tipping_value = tipping_value.max_tipping_value; + let stake_required = tipping_value.stake_required; + let funding_needed = max_tipping_value - 100; + let content: Content = Content::IPFS( + "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" + .as_bytes() + .to_vec(), + ); + assert_ok!(ProjectTips::create_project( + RuntimeOrigin::signed(who_ask_tipper), + 2, + content, + tipping_name, + funding_needed + )); + + let project_ids = ProjectTips::get_projects_from_accounts(who_ask_tipper); + let project_id = project_ids.last().unwrap(); + let project_id = *project_id; + // println!("project id {project_id}"); + + let balance = Balances::free_balance(who_ask_tipper); + + assert_ok!(ProjectTips::apply_staking_period( + RuntimeOrigin::signed(who_ask_tipper), + project_id + )); + + let after_balance = Balances::free_balance(who_ask_tipper); + + assert_eq!(after_balance, balance - stake_required); + + let phase_data = ProjectTips::get_phase_data(); + + for j in 4..30 { + assert_ok!(ProjectTips::apply_jurors( + RuntimeOrigin::signed(j), + project_id, + j * 100 + )); + } + + assert_noop!( + ProjectTips::draw_jurors(RuntimeOrigin::signed(5), project_id, 5), + >::PeriodDontMatch + ); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), project_id), + >::StakingPeriodNotOver + ); + + System::set_block_number(start_block_number + phase_data.staking_length); + + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + assert_ok!(ProjectTips::draw_jurors( + RuntimeOrigin::signed(5), + project_id, + 5 + )); + + let key = SumTreeName::ProjectTips { + project_id: project_id, + block_number: start_block_number, + }; + + let draws_in_round = SchellingGameShared::draws_in_round(key.clone()); + // println!("Draws in round{draws_in_round}"); + assert_eq!(5, draws_in_round); + + let drawn_jurors = SchellingGameShared::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + let period = SchellingGameShared::get_period(key.clone()); + + assert_eq!(Some(Period::Commit), period); + + assert_ok!(ProjectTips::unstaking(RuntimeOrigin::signed(5), project_id)); + + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_noop!( + ProjectTips::commit_vote(RuntimeOrigin::signed(6), project_id, hash), + >::JurorDoesNotExists + ); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(4), + project_id, + hash + )); + + // You can replace vote within the commit period. + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(4), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(7), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(13), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("0salt5".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(14), + project_id, + hash + )); + + let hash = sp_io::hashing::keccak_256("1salt6".as_bytes()); + assert_ok!(ProjectTips::commit_vote( + RuntimeOrigin::signed(15), + project_id, + hash + )); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), project_id), + >::CommitPeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + + start_block_number + + phase_data.staking_length + + phase_data.commit_length, + ); + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + assert_noop!( + ProjectTips::reveal_vote( + RuntimeOrigin::signed(4), + project_id, + 2, + "salt2".as_bytes().to_vec() + ), + >::CommitDoesNotMatch + ); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(4), + project_id, + 1, + "salt2".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(7), + project_id, + 1, + "salt3".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(14), + project_id, + 0, + "salt5".as_bytes().to_vec() + )); + + assert_ok!(ProjectTips::reveal_vote( + RuntimeOrigin::signed(15), + project_id, + 1, + "salt6".as_bytes().to_vec() + )); + + assert_noop!( + ProjectTips::pass_period(RuntimeOrigin::signed(5), project_id), + >::VotePeriodNotOver + ); + System::set_block_number( + phase_data.evidence_length + + start_block_number + + phase_data.staking_length + + phase_data.commit_length + + phase_data.vote_length, + ); + assert_ok!(ProjectTips::pass_period( + RuntimeOrigin::signed(5), + project_id + )); + + assert_noop!( + ProjectTips::add_incentive_count(RuntimeOrigin::signed(13), project_id), + >::VoteNotRevealed + ); + assert_ok!(ProjectTips::add_incentive_count( + RuntimeOrigin::signed(14), + project_id + )); + assert_ok!(ProjectTips::add_incentive_count( + RuntimeOrigin::signed(15), + project_id + )); +} + +#[test] +fn schelling_game_incentives_get_test() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + full_schelling_game_func(2, 1); + System::set_block_number(1000); + full_schelling_game_func(3, 1000); + + let incentive_count = ProjectTips::incentives_count(14).unwrap(); + + let incentive_count_eq: Incentives = Incentives { + number_of_games: 2, + winner: 2, + loser: 0, + total_stake: 14 * 100 + 14 * 100, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + assert_eq!(incentive_count, incentive_count_eq); + // println!("{:?}", incentive_count); + + let incentive_count = ProjectTips::incentives_count(15).unwrap(); + + // println!("{:?}", incentive_count); + + let incentive_count_eq: Incentives = Incentives { + number_of_games: 2, + winner: 0, + loser: 2, + total_stake: 15 * 100 + 15 * 100, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + assert_eq!(incentive_count, incentive_count_eq); + + // draw twenty schelling game + // use 14 and 15, increase both loser and winner count. + + assert_noop!( + ProjectTips::get_incentives(RuntimeOrigin::signed(15)), + Error::::NotReachedMinimumDecision + ); + + System::set_block_number(2000); + + full_schelling_game_func2(4, 2000); + + System::set_block_number(3000); + + full_schelling_game_func2(5, 3000); + + let incentive_count = ProjectTips::incentives_count(14).unwrap(); + + // println!("incentive count:{:?}", incentive_count); + + let incentive_count_eq: Incentives = Incentives { + number_of_games: 4, + winner: 2, + loser: 2, + total_stake: 14 * 100 + 14 * 100 + 14 * 100 + 14 * 100, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + assert_eq!(incentive_count, incentive_count_eq); + + let incentive_count = ProjectTips::incentives_count(15).unwrap(); + + // println!("{:?}", incentive_count); + + let incentive_count_eq: Incentives = Incentives { + number_of_games: 4, + winner: 2, + loser: 2, + total_stake: 15 * 100 + 15 * 100 + 15 * 100 + 15 * 100, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + assert_eq!(incentive_count, incentive_count_eq); + for x in 4..20 { + System::set_block_number(x * 1000); + full_schelling_game_func(x, x * 1000); + } + + let incentive_count = ProjectTips::incentives_count(14).unwrap(); + + let incentive_count_eq: Incentives = Incentives { + number_of_games: 20, + winner: 18, + loser: 2, + total_stake: 14 * 100 *20, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + assert_eq!(incentive_count, incentive_count_eq); + // println!("incentive count:{:?}", incentive_count); + + let balance = Balances::free_balance(14); + + // println!("balance account before(14):{:?}", balance); + + assert_ok!(ProjectTips::get_incentives(RuntimeOrigin::signed(14))); + + let balance = Balances::free_balance(14); + + // println!("balance account after(14):{:?}", balance); + + let incentive_count = ProjectTips::incentives_count(15).unwrap(); + + let incentive_count_eq: Incentives = Incentives { + number_of_games: 20, + winner: 2, + loser: 18, + total_stake: 14 * 100 *20, + start: WhenDetails { + block: 201, + time: 0, + }, + }; + + assert_eq!(incentive_count, incentive_count_eq); + // println!("incentive count:{:?}", incentive_count); + + let balance = Balances::free_balance(15); + + // println!("balance account before(15):{:?}", balance); + + assert_ok!(ProjectTips::get_incentives(RuntimeOrigin::signed(15))); + + let balance = Balances::free_balance(15); + + // println!("balance account after(15):{:?}", balance); + }) +} diff --git a/custom-pallets/project-tips/src/types.rs b/custom-pallets/project-tips/src/types.rs new file mode 100644 index 0000000..76412e1 --- /dev/null +++ b/custom-pallets/project-tips/src/types.rs @@ -0,0 +1,61 @@ +use super::*; +use frame_support::pallet_prelude::*; +use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use scale_info::TypeInfo; + +pub const PROJECT_ID: ProjectId = 1; + +#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub enum TippingName { + SmallTipper, + BigTipper, + SmallSpender, + MediumSpender, + BigSpender, +} + +#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct TippingValue { + pub max_tipping_value: Balance, + pub stake_required: Balance, +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct Project { + pub created: WhoAndWhenOf, + pub project_id: ProjectId, + pub department_id: DepartmentId, + pub content: Content, + pub tipping_name: TippingName, + pub funding_needed: BalanceOf, + pub project_leader: T::AccountId, +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct Incentives { + pub number_of_games: u64, + pub winner: u64, + pub loser: u64, + pub total_stake: u64, + pub start: WhenDetailsOf, +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct IncentivesMetaData { + pub total_number: u64, + pub disincentive_times: u64, + pub total_block: BlockNumberOf, +} + +impl Default for IncentivesMetaData { + fn default() -> Self { + Self { + total_number: 20, + disincentive_times: 15, // its 1.5 + total_block: 432000u64.saturated_into::>(), // 30 days = (24*60*60)/6 * 30 + } + } +} diff --git a/pallets/posts/src/weights.rs b/custom-pallets/project-tips/src/weights.rs similarity index 100% rename from pallets/posts/src/weights.rs rename to custom-pallets/project-tips/src/weights.rs diff --git a/custom-pallets/schelling-game-shared/Cargo.toml b/custom-pallets/schelling-game-shared/Cargo.toml new file mode 100644 index 0000000..b310622 --- /dev/null +++ b/custom-pallets/schelling-game-shared/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "pallet-schelling-game-shared" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +pallet-balances = { workspace = true } +sp-io = { workspace = true } +num-integer = { workspace = true } +pallet-sortition-sum-game = { workspace = true} +trait-sortition-sum-game = { workspace = true} +trait-schelling-game-shared = { workspace = true } + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +frame-support-test = { workspace = true } + + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-std/std", + "pallet-balances/std", + "sp-io/std", + "num-integer/std", + "pallet-sortition-sum-game/std", + "trait-sortition-sum-game/std", + "trait-schelling-game-shared/std", + "frame-support-test/std", +] + + +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] + diff --git a/pallets/project-tips/README.md b/custom-pallets/schelling-game-shared/README.md similarity index 100% rename from pallets/project-tips/README.md rename to custom-pallets/schelling-game-shared/README.md diff --git a/custom-pallets/schelling-game-shared/src/benchmarking.rs b/custom-pallets/schelling-game-shared/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/schelling-game-shared/src/docimage/change_period_link_1.svg b/custom-pallets/schelling-game-shared/src/docimage/change_period_link_1.svg similarity index 100% rename from pallets/schelling-game-shared/src/docimage/change_period_link_1.svg rename to custom-pallets/schelling-game-shared/src/docimage/change_period_link_1.svg diff --git a/pallets/schelling-game-shared/src/docimage/set_to_staking_period_1.svg b/custom-pallets/schelling-game-shared/src/docimage/set_to_staking_period_1.svg similarity index 100% rename from pallets/schelling-game-shared/src/docimage/set_to_staking_period_1.svg rename to custom-pallets/schelling-game-shared/src/docimage/set_to_staking_period_1.svg diff --git a/custom-pallets/schelling-game-shared/src/extras.rs b/custom-pallets/schelling-game-shared/src/extras.rs new file mode 100644 index 0000000..6420802 --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/extras.rs @@ -0,0 +1,948 @@ +use crate::*; + +impl Pallet { + pub(super) fn create_phase_with_all_data( + evidence_length: u64, + end_of_staking_time: u64, + staking_length: u64, + drawing_length: u64, + commit_length: u64, + vote_length: u64, + appeal_length: u64, + max_draws: u64, + min_number_juror_staked: u64, + min_juror_stake: u64, + juror_incentives: (u64, u64), + ) -> PhaseDataOf { + PhaseData::create_phase_with_all_data( + evidence_length, + end_of_staking_time, + staking_length, + drawing_length, + commit_length, + vote_length, + appeal_length, + max_draws, + min_number_juror_staked, + min_juror_stake, + juror_incentives, + ) + } + pub(super) fn create_phase_data( + block_length: u64, + max_draws: u64, + min_number_juror_staked: u64, + min_juror_stake: u64, + juror_incentives: (u64, u64), + ) -> PhaseDataOf { + PhaseData::create_with_data( + block_length, + max_draws, + min_number_juror_staked, + min_juror_stake, + juror_incentives, + ) + } + /// Set to evidence period, when some one stakes for validation + pub(super) fn set_to_evidence_period( + key: SumTreeNameType, + now: BlockNumberOf, + ) -> DispatchResult { + match >::get(&key) { + Some(_period) => Err(Error::::PeriodExists)?, + None => { + let period = Period::Evidence; + >::insert(&key, period); + >::insert(&key, now); + } + } + Ok(()) + } + + /// Check `Period` is `Evidence`, and change it to `Staking` + /// It is called with function that submits challenge stake after `end_block` of evidence period + /// Checks evidence period is over + #[doc=include_str!("docimage/set_to_staking_period_1.svg")] + /// ```ignore + /// if time >= block_time.min_short_block_length { + /// // change `Period` to `Staking` + /// } + /// ``` + pub(super) fn set_to_staking_period( + key: SumTreeNameType, + phase_data: PhaseDataOf, + now: BlockNumberOf, + ) -> DispatchResult { + if let Some(Period::Evidence) = >::get(&key) { + let evidence_stake_block_number = >::get(&key); + let time = now + .checked_sub(&evidence_stake_block_number) + .expect("Overflow"); + let evidence_length = phase_data.evidence_length; + let end_length_for_staking = phase_data.end_of_staking_time; + let total_length = evidence_length + .checked_add(&end_length_for_staking) + .expect("overflow"); + if time >= evidence_length && time < total_length { + let new_period = Period::Staking; + >::insert(&key, new_period); + >::insert(&key, now); + } else if time >= total_length { + Err(Error::::TimeForStakingOver)? + } else { + Err(Error::::EvidencePeriodNotOver)? + } + } else { + Err(Error::::PeriodIsNotEvidence)? + } + + Ok(()) + } + + /// Check time for staking over + pub(super) fn ensure_time_for_staking_over( + key: SumTreeNameType, + phase_data: PhaseDataOf, + now: BlockNumberOf, + ) -> DispatchResult { + let evidence_stake_block_number = >::get(&key); + let time = now + .checked_sub(&evidence_stake_block_number) + .expect("Overflow"); + let evidence_length = phase_data.evidence_length; + let end_length_for_staking = phase_data.end_of_staking_time; + let total_length = evidence_length + .checked_add(&end_length_for_staking) + .expect("overflow"); + ensure!(time >= total_length, Error::::TimeForStakingNotOver); + Ok(()) + } + + /// Set staking period when evidence period is not required + pub(super) fn set_to_staking_period_pe( + key: SumTreeNameType, + now: BlockNumberOf, + ) -> DispatchResult { + if let None = >::get(&key) { + let new_period = Period::Staking; + >::insert(&key, new_period); + >::insert(&key, now); + } else { + Err(Error::::PeriodIsNotNone)? + } + + Ok(()) + } + + pub(super) fn create_tree_link_helper(key: SumTreeNameType, k: u64) -> DispatchResult { + T::SortitionSumGameSource::create_tree_link(key.clone(), k)?; + Ok(()) + } + + /// Change the `Period` + /// + /// `Period::Staking` to `Period::Drawing` + #[doc=include_str!("docimage/change_period_link_1.svg")] + /// ```ignore + /// if now >= min_long_block_length + staking_start_time { + /// // Change `Period::Staking` to `Period::Drawing` + /// } + /// ``` + /// + /// `Period::Drawing` to `Period::Commit` + /// When maximum juror are drawn + /// + /// `Period::Commit` to `Period::Vote` + /// ```ignore + /// if now >= min_long_block_length + commit_start_time { + /// // Change `Period::Commit` to `Period::Vote` + /// } + /// ``` + /// + /// `Period::Vote` to `Period::Execution` + /// ```ignore + /// if now >= min_long_block_length + vote_start_time { + /// // Change `Period::Vote` to `Period::Execution` + /// } + /// ``` + pub(super) fn change_period( + key: SumTreeNameType, + phase_data: PhaseDataOf, + now: BlockNumberOf, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + match period { + Period::Evidence => todo!(), + Period::Staking => { + // Also check has min number of jurors has staked + let staking_start_time = >::get(&key); + let staking_length = phase_data.staking_length; + if now >= staking_length + staking_start_time { + let new_period = Period::Drawing; + >::insert(&key, new_period); + } else { + Err(Error::::StakingPeriodNotOver)? + } + } + Period::Drawing => { + // Also give time + let max_draws = phase_data.max_draws; + let draws_in_round = >::get(&key); + if draws_in_round >= max_draws { + >::insert(&key, now); + let new_period = Period::Commit; + >::insert(&key, new_period); + } else { + Err(Error::::MaxJurorNotDrawn)? + } + } + Period::Commit => { + let commit_start_time = >::get(&key); + let commit_length = phase_data.commit_length; + if now >= commit_length + commit_start_time { + >::insert(&key, now); + let new_period = Period::Vote; + >::insert(&key, new_period); + } else { + Err(Error::::CommitPeriodNotOver)? + } + } + Period::Vote => { + let vote_start_time = >::get(&key); + let vote_length = phase_data.vote_length; + if now >= vote_length + vote_start_time { + let new_period = Period::Execution; + >::insert(&key, new_period); + } else { + Err(Error::::VotePeriodNotOver)? + } + } + Period::Appeal => todo!(), + Period::Execution => todo!(), + } + } + None => Err(Error::::PeriodDoesNotExists)?, + } + Ok(()) + } + + pub(super) fn apply_jurors_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + who: AccountIdOf, + stake: BalanceOf, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Staking, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + let min_stake = phase_data.min_juror_stake; + + ensure!(stake >= min_stake, Error::::JurorStakeLessThanMin); + + // let imb = T::Currency::withdraw( + // &who, + // stake, + // WithdrawReasons::TRANSFER, + // ExistenceRequirement::AllowDeath, + // )?; + + // T::Currency::resolve_creating(&Self::juror_stake_account(), imb); + + let imbalance = T::Currency::slash(&who, stake).0; + T::Slash::on_unbalanced(imbalance); + + // let stake_of = Self::stake_of(key.clone(), profile_citizenid)?; + + let stake_u64 = Self::balance_to_u64_saturated(stake); + + let stake_of = T::SortitionSumGameSource::stake_of_link(key.clone(), who.clone())?; + + match stake_of { + Some(_stake) => Err(Error::::AlreadyStaked)?, + None => { + let result = T::SortitionSumGameSource::set_link(key, stake_u64, who); + result + } + } + } + + // Improvements: Set stake to zero after a juror is drawn, so that they are not drawn again. Store the stake in storage map in DrawnJurors, and use it in get_incentives_helper + pub(super) fn draw_jurors_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + iterations: u64, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Drawing, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + let max_draws = phase_data.max_draws; + let draws_in_round = >::get(&key); + ensure!( + draws_in_round < max_draws.into(), + Error::::MaxDrawExceeded + ); + let mut end_index = draws_in_round + iterations; + if draws_in_round + iterations >= max_draws { + end_index = max_draws; + } + let mut draw_increment = draws_in_round.clone(); + + for _ in draws_in_round..end_index { + let nonce = Self::get_and_increment_nonce(); + let random_seed = T::RandomnessSource::random(&nonce).encode(); + let random_number = u64::decode(&mut random_seed.as_ref()) + .expect("secure hashes should always be bigger than u64; qed"); + // let mut rng = rand::thread_rng(); + // let random_number: u64 = rng.gen(); + // log::info!("Random number: {:?}", random_number); + let accountid = T::SortitionSumGameSource::draw_link(key.clone(), random_number)?; + let stake = T::SortitionSumGameSource::stake_of_link(key.clone(), accountid.clone())?; + + let mut drawn_juror = >::get(&key); + match drawn_juror.binary_search_by(|(c, _)| c.cmp(&accountid)) { + Ok(_) => {} + Err(index) => { + drawn_juror.insert(index, (accountid.clone(), stake.unwrap())); + >::insert(&key, drawn_juror); + draw_increment = draw_increment + 1; + // println!("draw_increment, {:?}", draw_increment); + T::SortitionSumGameSource::set_link(key.clone(), 0, accountid)?; + } + } + >::insert(&key, draw_increment); + } + Ok(()) + } + + // When DrawnJurors contains stake, use drawn_juror.binary_search_by(|(c, _)| c.cmp(&who)); + pub(super) fn unstaking_helper(key: SumTreeNameType, who: AccountIdOf) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!( + period == Period::Commit + || period == Period::Vote + || period == Period::Execution, + Error::::PeriodDontMatch + ); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + + let drawn_juror = >::get(&key); + match drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) { + Ok(_) => Err(Error::::SelectedAsJuror)?, + Err(_) => {} + } + + let stake_of = T::SortitionSumGameSource::stake_of_link(key.clone(), who.clone())?; + + match stake_of { + Some(stake) => { + let balance = Self::u64_to_balance_saturated(stake); + let mut unstaked_jurors = >::get(&key); + match unstaked_jurors.binary_search(&who) { + Ok(_) => Err(Error::::AlreadyUnstaked)?, + Err(index) => { + unstaked_jurors.insert(index, who.clone()); + >::insert(&key, unstaked_jurors); + // let _ = T::Currency::resolve_into_existing( + // &who, + // T::Currency::withdraw( + // &Self::juror_stake_account(), + // balance, + // WithdrawReasons::TRANSFER, + // ExistenceRequirement::AllowDeath, + // )?, + // ); + let r = T::Currency::deposit_into_existing(&who, balance) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + } + } + } + None => Err(Error::::StakeDoesNotExists)?, + } + + // println!("stakeof {:?}", stake_of); + + Ok(()) + } + + pub(super) fn commit_vote_helper( + key: SumTreeNameType, + who: AccountIdOf, + vote_commit: [u8; 32], + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Commit, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + let drawn_jurors = >::get(&key); + match drawn_jurors.binary_search_by(|(c, _)| c.cmp(&who.clone())) { + Ok(_) => { + let vote_commit_struct = CommitVote { + commit: vote_commit, + votestatus: VoteStatus::Commited, + revealed_vote: None, + }; + >::insert(&key, &who, vote_commit_struct); + } + Err(_) => Err(Error::::JurorDoesNotExists)?, + } + Ok(()) + } + + pub(super) fn reveal_vote_two_choice_helper( + key: SumTreeNameType, + who: AccountIdOf, + choice: u128, + salt: Vec, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Vote, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + let who_commit_vote = >::get(&key, &who); + match who_commit_vote { + Some(mut commit_struct) => { + ensure!( + commit_struct.votestatus == VoteStatus::Commited, + Error::::VoteStatusNotCommited + ); + let mut vote = format!("{}", choice).as_bytes().to_vec(); + // let mut vote = choice.clone(); + let mut salt_a = salt.clone(); + vote.append(&mut salt_a); + let vote_bytes: &[u8] = &vote; + let hash = sp_io::hashing::keccak_256(vote_bytes); + let commit: &[u8] = &commit_struct.commit; + if hash == commit { + let mut decision_tuple = >::get(&key); + if choice == 1 { + decision_tuple.1 = decision_tuple.1 + 1; + >::insert(&key, decision_tuple); + commit_struct.revealed_vote = Some(RevealedVote::Yes); + } else if choice == 0 { + decision_tuple.0 = decision_tuple.0 + 1; + >::insert(&key, decision_tuple); + commit_struct.revealed_vote = Some(RevealedVote::No); + } else { + Err(Error::::NotValidChoice)? + } + commit_struct.votestatus = VoteStatus::Revealed; + >::insert(&key, &who, commit_struct); + } else { + Err(Error::::CommitDoesNotMatch)? + } + } + None => Err(Error::::CommitDoesNotExists)?, + } + + Ok(()) + } + + /// Distribute incentives in a single go. + pub(super) fn get_all_incentives_two_choice_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Execution, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + + let drawn_jurors = >::get(&key); + let reveal_votes_iterator = >::iter_prefix(&key); + + let mut reveal_votes = reveal_votes_iterator + .map(|(account_id, commit_vote)| (account_id, commit_vote.revealed_vote)) + .collect::>(); + reveal_votes.sort_by(|a, b| a.0.cmp(&b.0)); + let decision_count = >::get(&key); + let incentives = phase_data.juror_incentives; + let (winning_decision, winning_incentives) = + Self::get_winning_incentives(decision_count, incentives); + for juror in drawn_jurors { + match reveal_votes.binary_search_by(|(c, _)| c.cmp(&juror.0)) { + Ok(index) => { + let account_n_vote = reveal_votes[index].clone(); + if let Some(vote) = account_n_vote.1 { + match winning_decision { + WinningDecision::WinnerYes => match vote { + RevealedVote::Yes => { + let result = Self::winner_getting_incentives2( + juror.0.clone(), + winning_incentives, + juror.1, + )?; + result + } + RevealedVote::No => { + let result = + Self::looser_getting_incentives2(juror.0.clone(), juror.1)?; + result + } + }, + WinningDecision::WinnerNo => match vote { + RevealedVote::Yes => { + let result = + Self::looser_getting_incentives2(juror.0.clone(), juror.1)?; + result + } + RevealedVote::No => { + let result = Self::winner_getting_incentives2( + juror.0.clone(), + winning_incentives, + juror.1, + )?; + result + } + }, + WinningDecision::Draw => { + let result = + Self::getting_incentives_draw2(juror.0.clone(), juror.1)?; + result + } + } + } + } + Err(_) => {} + } + } + // Remove SorititionSumTrees in `sortition-sum-game` pallet + let _result = T::SortitionSumGameSource::remove_tree_link(key.clone()); + + // Remove DrawnJurors + >::remove(&key); + + // Remove VoteCommits + >::remove_prefix(key.clone(), None); // Deprecated: Use clear_prefix instead + // let reveal_votes_iterator2 = >::iter_prefix(&key); + // reveal_votes_iterator2.for_each(|(account_id, _)|{ + // >::remove(key.clone(), account_id); + // }); + + Ok(()) + } + + // Improvements: Will it be better to distribute all jurors incentives in single call + pub(super) fn get_incentives_two_choice_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + who: AccountIdOf, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Execution, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + + let drawn_juror = >::get(&key); + + let who_commit_vote = >::get(&key, &who); + match who_commit_vote { + Some(commit_struct) => { + let vote_option = commit_struct.revealed_vote; + match vote_option { + Some(vote) => { + let decision_count: (u64, u64) = >::get(&key); + let incentives = phase_data.juror_incentives; + let (winning_decision, winning_incentives) = + Self::get_winning_incentives(decision_count, incentives); + if let Ok(i) = drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) { + let stake = drawn_juror[i].1; + match winning_decision { + WinningDecision::WinnerYes => match vote { + RevealedVote::Yes => { + let result = Self::winner_getting_incentives( + key.clone(), + who.clone(), + winning_incentives, + stake, + )?; + result + } + RevealedVote::No => { + let result = Self::looser_getting_incentives( + key.clone(), + who.clone(), + stake, + )?; + result + } + }, + WinningDecision::WinnerNo => match vote { + RevealedVote::Yes => { + let result = Self::looser_getting_incentives( + key.clone(), + who.clone(), + stake, + )?; + result + } + RevealedVote::No => { + let result = Self::winner_getting_incentives( + key.clone(), + who.clone(), + winning_incentives, + stake, + )?; + result + } + }, + WinningDecision::Draw => { + let result = Self::getting_incentives_draw( + key.clone(), + who.clone(), + stake.clone(), + )?; + result + } + } + } else { + Err(Error::::StakeDoesNotExists)? + } + } + None => Err(Error::::VoteNotRevealed)?, + } + } + None => Err(Error::::CommitDoesNotExists)?, + } + Ok(()) + } + + pub(super) fn get_result_of_juror( + key: SumTreeNameType, + who: AccountIdOf, + ) -> Result<(JurorGameResult, u64), DispatchError> { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Execution, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + + let drawn_juror = >::get(&key); + + let who_commit_vote = >::get(&key, &who); + match who_commit_vote { + Some(commit_struct) => { + let vote_option = commit_struct.revealed_vote; + match vote_option { + Some(vote) => { + let decision_count: (u64, u64) = >::get(&key); + let winning_decision = Self::get_winning_decision(decision_count); + if let Ok(i) = drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) { + let stake = drawn_juror[i].1; + + match winning_decision { + WinningDecision::WinnerYes => match vote { + RevealedVote::Yes => Ok((JurorGameResult::Won, stake)), + RevealedVote::No => Ok((JurorGameResult::Lost, stake)), + }, + WinningDecision::WinnerNo => match vote { + RevealedVote::Yes => Ok((JurorGameResult::Lost, stake)), + RevealedVote::No => Ok((JurorGameResult::Won, stake)), + }, + WinningDecision::Draw => Ok((JurorGameResult::Draw, stake)), + } + } else { + Err(Error::::StakeDoesNotExists)? + } + } + None => Err(Error::::VoteNotRevealed)?, + } + } + None => Err(Error::::CommitDoesNotExists)?, + } + } + + pub(super) fn add_to_incentives_count( + key: SumTreeNameType, + who: AccountIdOf, + ) -> DispatchResult { + let mut juror_got_incentives = >::get(&key); + match juror_got_incentives.binary_search(&who) { + Ok(_) => Err(Error::::AlreadyIncentivesAdded)?, + Err(index) => { + juror_got_incentives.insert(index, who.clone()); + >::insert(&key, juror_got_incentives); + } + } + Ok(()) + } + + pub(super) fn getting_incentives_draw( + key: SumTreeNameType, + who: AccountIdOf, + stake: u64, + ) -> DispatchResult { + let balance = Self::u64_to_balance_saturated(stake); + let mut juror_got_incentives = >::get(&key); + match juror_got_incentives.binary_search(&who) { + Ok(_) => Err(Error::::AlreadyGotIncentives)?, + Err(index) => { + juror_got_incentives.insert(index, who.clone()); + >::insert(&key, juror_got_incentives); + let r = T::Currency::deposit_into_existing(&who, balance) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + } + } + + Ok(()) + } + + pub(super) fn getting_incentives_draw2(who: AccountIdOf, stake: u64) -> DispatchResult { + let balance = Self::u64_to_balance_saturated(stake); + + let r = T::Currency::deposit_into_existing(&who, balance) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + + Ok(()) + } + + pub(super) fn looser_getting_incentives( + key: SumTreeNameType, + who: AccountIdOf, + stake: u64, + ) -> DispatchResult { + let balance = Self::u64_to_balance_saturated(stake * 3 / 4); + let mut juror_got_incentives = >::get(&key); + match juror_got_incentives.binary_search(&who) { + Ok(_) => Err(Error::::AlreadyGotIncentives)?, + Err(index) => { + juror_got_incentives.insert(index, who.clone()); + >::insert(&key, juror_got_incentives); + let r = T::Currency::deposit_into_existing(&who, balance) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + } + } + Ok(()) + } + + pub(super) fn looser_getting_incentives2(who: AccountIdOf, stake: u64) -> DispatchResult { + let balance = Self::u64_to_balance_saturated(stake * 3 / 4); + + let r = T::Currency::deposit_into_existing(&who, balance) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + + Ok(()) + } + + pub(super) fn winner_getting_incentives( + key: SumTreeNameType, + who: AccountIdOf, + winning_incentives: u64, + stake: u64, + ) -> DispatchResult { + let mut juror_got_incentives = >::get(&key); + match juror_got_incentives.binary_search(&who) { + Ok(_) => Err(Error::::AlreadyGotIncentives)?, + Err(index) => { + juror_got_incentives.insert(index, who.clone()); + >::insert(&key, juror_got_incentives); + let total_incentives = stake.checked_add(winning_incentives).expect("overflow"); + let incentives = Self::u64_to_balance_saturated(total_incentives); + let r = T::Currency::deposit_into_existing(&who, incentives) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + } + }; + + Ok(()) + } + + pub(super) fn winner_getting_incentives2( + who: AccountIdOf, + winning_incentives: u64, + stake: u64, + ) -> DispatchResult { + let total_incentives = stake.checked_add(winning_incentives).expect("overflow"); + let incentives = Self::u64_to_balance_saturated(total_incentives); + let r = T::Currency::deposit_into_existing(&who, incentives) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + + Ok(()) + } + + pub(super) fn get_winning_decision(decision_tuple: (u64, u64)) -> WinningDecision { + if decision_tuple.1 > decision_tuple.0 { + WinningDecision::WinnerYes // Decision 1 won + } else if decision_tuple.0 > decision_tuple.1 { + WinningDecision::WinnerNo // Decision 0 won + } else { + WinningDecision::Draw // draw + } + } + + pub(super) fn get_winning_decision_value(key: SumTreeNameType) -> WinningDecision { + let decision_tuple: (u64, u64) = >::get(&key); + Self::get_winning_decision(decision_tuple) + } + + pub(super) fn get_winning_incentives( + decision_tuple: (u64, u64), + incentive_tuple: (u64, u64), + ) -> (WinningDecision, u64) { + let winning_decision = Self::get_winning_decision(decision_tuple); + match winning_decision { + WinningDecision::WinnerYes => { + let winning_incentives = (incentive_tuple.1) + .checked_div(decision_tuple.1) + .expect("Overflow"); + (WinningDecision::WinnerYes, winning_incentives) + } + WinningDecision::WinnerNo => { + let winning_incentives = (incentive_tuple.1) + .checked_div(decision_tuple.0) + .expect("Overflow"); + (WinningDecision::WinnerNo, winning_incentives) + } + WinningDecision::Draw => (WinningDecision::Draw, 0), + } + } + + pub(super) fn balance_to_u64_saturated(input: BalanceOf) -> u64 { + input.saturated_into::() + } + + pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { + input.saturated_into::>() + } + + pub(super) fn block_number_to_u32_saturated(input: BlockNumberOf) -> u32 { + input.saturated_into::() + } + pub(super) fn get_and_increment_nonce() -> Vec { + let nonce = >::get(); + >::put(nonce.wrapping_add(1)); + // let n = nonce * 1000 + 1000; // remove and uncomment in production + // n.encode() + + nonce.encode() + } + pub(super) fn get_evidence_period_end_block_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + now: BlockNumberOf, + ) -> Option { + let start_block_number = >::get(&key); + let evidence_length = phase_data.evidence_length; + let end_block = start_block_number + .checked_add(&evidence_length) + .expect("Overflow"); + let left_block = end_block.checked_sub(&now); + match left_block { + Some(val) => { + let left_block_u32 = Self::block_number_to_u32_saturated(val); + Some(left_block_u32) + } + None => Some(0), + } + } + + pub(super) fn get_staking_period_end_block_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + now: BlockNumberOf, + ) -> Option { + let staking_start_time = >::get(&key); + let staking_length = phase_data.staking_length; + let end_block = staking_start_time + .checked_add(&staking_length) + .expect("Overflow"); + let left_block = end_block.checked_sub(&now); + match left_block { + Some(val) => { + let left_block_u32 = Self::block_number_to_u32_saturated(val); + Some(left_block_u32) + } + None => Some(0), + } + } + + pub(super) fn get_drawing_period_end_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + ) -> (u64, u64, bool) { + let max_draws = phase_data.max_draws; + let draws_in_round = >::get(&key); + if draws_in_round >= max_draws.into() { + (max_draws, draws_in_round, true) + } else { + (max_draws, draws_in_round, false) + } + } + + pub(super) fn get_commit_period_end_block_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + now: BlockNumberOf, + ) -> Option { + let commit_start_time = >::get(&key); + let commit_length = phase_data.commit_length; + let end_block = commit_start_time + .checked_add(&commit_length) + .expect("Overflow"); + let left_block = end_block.checked_sub(&now); + match left_block { + Some(val) => { + let left_block_u32 = Self::block_number_to_u32_saturated(val); + Some(left_block_u32) + } + None => Some(0), + } + } + + pub(super) fn get_vote_period_end_block_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + now: BlockNumberOf, + ) -> Option { + let vote_start_time = >::get(&key); + let vote_length = phase_data.vote_length; + let end_block = vote_start_time.checked_add(&vote_length).expect("Overflow"); + let left_block = end_block.checked_sub(&now); + match left_block { + Some(val) => { + let left_block_u32 = Self::block_number_to_u32_saturated(val); + Some(left_block_u32) + } + None => Some(0), + } + } + + pub(super) fn selected_as_juror_helper(key: SumTreeNameType, who: T::AccountId) -> bool { + let drawn_juror = >::get(&key); + match drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) { + Ok(_) => true, + Err(_) => false, + } + } +} diff --git a/custom-pallets/schelling-game-shared/src/functions.rs b/custom-pallets/schelling-game-shared/src/functions.rs new file mode 100644 index 0000000..6591fa7 --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/functions.rs @@ -0,0 +1,114 @@ +use super::*; + +// 6 sec (1 block) +// 3 days (43200), 10 days (144000) +// 15 mins (150) +// 5 mins (50) +// 8 mins (80) + +impl PhaseData { + pub fn new( + evidence_length: BlockNumberOf, + end_of_staking_time: BlockNumberOf, + staking_length: BlockNumberOf, + drawing_length: BlockNumberOf, + commit_length: BlockNumberOf, + vote_length: BlockNumberOf, + appeal_length: BlockNumberOf, + max_draws: u64, + min_number_juror_staked: u64, + min_juror_stake: BalanceOf, + juror_incentives: (u64, u64), + ) -> Self { + PhaseData { + evidence_length, + end_of_staking_time, + staking_length, + drawing_length, + commit_length, + vote_length, + appeal_length, + max_draws, + min_number_juror_staked, + min_juror_stake, + juror_incentives, + } + } + + pub fn default() -> Self { + PhaseData { + evidence_length: 144000u64.saturated_into::>(), + end_of_staking_time: 144000u64.saturated_into::>(), + staking_length: 144000u64.saturated_into::>(), + drawing_length: 144000u64.saturated_into::>(), + commit_length: 144000u64.saturated_into::>(), + vote_length: 144000u64.saturated_into::>(), + appeal_length: 144000u64.saturated_into::>(), + max_draws: 30, + min_number_juror_staked: 50, + min_juror_stake: 1000u64.saturated_into::>(), + juror_incentives: (1000, 1000), + } + } + + pub fn create_with_data( + block_length: u64, + max_draws: u64, + min_number_juror_staked: u64, + min_juror_stake: u64, + juror_incentives: (u64, u64), + ) -> Self { + let block_length = block_length.saturated_into::>(); + let min_juror_stake = min_juror_stake.saturated_into::>(); + PhaseData { + evidence_length: block_length, + end_of_staking_time: block_length, + staking_length: block_length, + drawing_length: block_length, + commit_length: block_length, + vote_length: block_length, + appeal_length: block_length, + max_draws, + min_number_juror_staked, + min_juror_stake, + juror_incentives, + } + } + + pub fn create_phase_with_all_data( + evidence_length: u64, + end_of_staking_time: u64, + staking_length: u64, + drawing_length: u64, + commit_length: u64, + vote_length: u64, + appeal_length: u64, + max_draws: u64, + min_number_juror_staked: u64, + min_juror_stake: u64, + juror_incentives: (u64, u64), + ) -> Self { + let evidence_length = evidence_length.saturated_into::>(); + let end_of_staking_time = end_of_staking_time.saturated_into::>(); + let staking_length = staking_length.saturated_into::>(); + let drawing_length = drawing_length.saturated_into::>(); + let commit_length = commit_length.saturated_into::>(); + let vote_length = vote_length.saturated_into::>(); + let appeal_length = appeal_length.saturated_into::>(); + + let min_juror_stake = min_juror_stake.saturated_into::>(); + PhaseData { + evidence_length, + end_of_staking_time, + staking_length, + drawing_length, + commit_length, + vote_length, + appeal_length, + max_draws, + min_number_juror_staked, + min_juror_stake, + juror_incentives, + } + } +} diff --git a/custom-pallets/schelling-game-shared/src/lib.rs b/custom-pallets/schelling-game-shared/src/lib.rs new file mode 100644 index 0000000..b87f443 --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/lib.rs @@ -0,0 +1,282 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +mod extras; +mod functions; +mod score_game; +mod share_link; +pub mod types; + +use crate::types::{ + CommitVote, JurorGameResult, Period, PhaseData, RangePoint, RevealedVote, SchellingGameType, + ScoreCommitVote, VoteStatus, WinningDecision, +}; +use frame_support::pallet_prelude::*; +use frame_support::sp_runtime::traits::{CheckedAdd, CheckedSub}; +use frame_support::sp_runtime::SaturatedConversion; +use frame_support::traits::Randomness; +use frame_support::traits::{Currency, OnUnbalanced, ReservableCurrency}; +use frame_system::pallet_prelude::*; +use num_integer::Roots; +use pallet_sortition_sum_game::types::SumTreeName; +use scale_info::prelude::format; +use sp_std::prelude::*; +use trait_sortition_sum_game::SortitionSumGameLink; + +pub type BlockNumberOf = BlockNumberFor; +type AccountIdOf = ::AccountId; +type BalanceOf = <::Currency as Currency>>::Balance; +type PositiveImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::PositiveImbalance; +type NegativeImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::NegativeImbalance; +type SumTreeNameType = SumTreeName, BlockNumberOf>; +type PhaseDataOf = PhaseData; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + + type SortitionSumGameSource: SortitionSumGameLink< + SumTreeName = SumTreeName>, + AccountId = Self::AccountId, + >; + + type Currency: ReservableCurrency; + + type RandomnessSource: Randomness>; + + /// Handler for the unbalanced increment when rewarding (minting rewards) + type Reward: OnUnbalanced>; + + /// Handler for the unbalanced decrement when slashing (burning collateral) + type Slash: OnUnbalanced>; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + #[pallet::storage] + pub type Nonce = StorageValue<_, u64, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn get_period)] + pub type PeriodName = StorageMap<_, Blake2_128Concat, SumTreeNameType, Period>; + + #[pallet::storage] + #[pallet::getter(fn draws_in_round)] + pub type DrawsInRound = StorageMap<_, Blake2_128Concat, SumTreeNameType, u64, ValueQuery>; // A counter of draws made in the current round. + + #[pallet::storage] + #[pallet::getter(fn evidence_start_time)] + pub type EvidenceStartTime = + StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn staking_start_time)] + pub type StakingStartTime = + StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn commit_start_time)] + pub type CommitStartTime = + StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn vote_start_time)] + pub type VoteStartTime = + StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; + + /// Drawn jurors containing account id and stake Vec<(AccountId, Stake)> + /// Should be stored in sorted order by AccountId + #[pallet::storage] + #[pallet::getter(fn drawn_jurors)] + pub type DrawnJurors = + StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec<(T::AccountId, u64)>, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn unstaked_jurors)] + pub type UnstakedJurors = + StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec, ValueQuery>; + + /// VoteCommits for Yes or No voting + #[pallet::storage] + #[pallet::getter(fn vote_commits)] + pub type VoteCommits = StorageDoubleMap< + _, + Blake2_128Concat, + SumTreeNameType, + Blake2_128Concat, + T::AccountId, + CommitVote, + >; + + /// Vote Commits for Score Schelling + #[pallet::storage] + #[pallet::getter(fn vote_commits_score)] + pub type ScoreVoteCommits = StorageDoubleMap< + _, + Blake2_128Concat, + SumTreeNameType, + Blake2_128Concat, + T::AccountId, + ScoreCommitVote, + >; + + /// Reveal values of score schelling game as Vec + #[pallet::storage] + #[pallet::getter(fn reveal_score_values)] + pub type RevealScoreValues = + StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec, ValueQuery>; + + /// New mean from the reveal values in score schelling game + /// Improvement: This step will not be required if all jurors incentives are distributed at one time + #[pallet::storage] + #[pallet::getter(fn new_mean_reveal_score)] + pub type IncentiveMeanRevealScore = + StorageMap<_, Blake2_128Concat, SumTreeNameType, i64>; + + /// Decision count for two choices after reveal vote: (count for 0, count for 1) + #[pallet::storage] + #[pallet::getter(fn decision_count)] + pub type DecisionCount = + StorageMap<_, Blake2_128Concat, SumTreeNameType, (u64, u64), ValueQuery>; // Count for 0, Count for 1 + + #[pallet::storage] + #[pallet::getter(fn juror_incentive_distribution)] + pub type JurorsIncentiveDistributedAccounts = + StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn incentive_added_to_count)] + pub type IncentiveAddedToCount = + StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec, ValueQuery>; + + // #[pallet::storage] + // #[pallet::getter(fn )] + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + PeriodExists, + EvidencePeriodNotOver, + StakingPeriodNotOver, + PeriodIsNotEvidence, + PeriodIsNotNone, + MaxJurorNotDrawn, + CommitPeriodNotOver, + VotePeriodNotOver, + PeriodDoesNotExists, + PeriodDontMatch, + JurorStakeLessThanMin, + AlreadyStaked, + MaxDrawExceeded, + SelectedAsJuror, + AlreadyUnstaked, + StakeDoesNotExists, + JurorDoesNotExists, + VoteStatusNotCommited, + NotValidChoice, + CommitDoesNotMatch, + CommitDoesNotExists, + AlreadyGotIncentives, + AlreadyIncentivesAdded, + VoteNotRevealed, + TimeForStakingOver, + TimeForStakingNotOver, + NewMeanNotInserted, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + /// An example dispatchable that takes a singles value as a parameter, writes the value to + /// storage and emits an event. This function must be dispatched by a signed extrinsic. + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + // This function will return an error if the extrinsic is not signed. + // https://docs.substrate.io/main-docs/build/origins/ + let who = ensure_signed(origin)?; + + // Update storage. + >::put(something); + + // Emit an event. + Self::deposit_event(Event::SomethingStored { something, who }); + // Return a successful DispatchResultWithPostInfo + Ok(()) + } + + /// An example dispatchable that may throw a custom error. + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn cause_error(origin: OriginFor) -> DispatchResult { + let _who = ensure_signed(origin)?; + + // Read a value from storage. + match >::get() { + // Return an error if the value has not been set. + None => return Err(Error::::NoneValue.into()), + Some(old) => { + // Increment the value read from storage; will error in the event of overflow. + let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; + // Update the value in storage with the incremented result. + >::put(new); + Ok(()) + } + } + } + } +} diff --git a/custom-pallets/schelling-game-shared/src/mock.rs b/custom-pallets/schelling-game-shared/src/mock.rs new file mode 100644 index 0000000..70cb60c --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/mock.rs @@ -0,0 +1,133 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; +use sp_std::vec; + +type Block = frame_system::mocking::MockBlock; +use frame_support_test::TestRandomness; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + TemplateModule: pallet_template, + Balances: pallet_balances, + SortitionSumGame: pallet_sortition_sum_game, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type AccountData = pallet_balances::AccountData; // New code +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; // New code + type RandomnessSource = TestRandomness; + type Slash = (); + type Reward = (); + type SortitionSumGameSource = SortitionSumGame; +} + +impl pallet_sortition_sum_game::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl pallet_balances::Config for Test { + type MaxHolds = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 100000), + (2, 200000), + (3, 300000), + (4, 300000), + (5, 300000), + (6, 300000), + (7, 300000), + (8, 300000), + (9, 300000), + (10, 300000), + (11, 300000), + (12, 300000), + (13, 300000), + (14, 300000), + (15, 300000), + (16, 300000), + (17, 300000), + (18, 300000), + (19, 300000), + (20, 300000), + (21, 300000), + (22, 300000), + (23, 300000), + (24, 300000), + (25, 300000), + (26, 300000), + (27, 300000), + (28, 300000), + (29, 300000), + (30, 300000), + (31, 300000), + (32, 300000), + (33, 300000), + (34, 300000), + (35, 300000), + ], + } // new code + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} diff --git a/custom-pallets/schelling-game-shared/src/score_game.rs b/custom-pallets/schelling-game-shared/src/score_game.rs new file mode 100644 index 0000000..4ade3d8 --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/score_game.rs @@ -0,0 +1,279 @@ +use crate::*; +use scale_info::prelude::vec; + +impl Pallet { + /// Commit your score vote + pub(super) fn commit_vote_for_score_helper( + key: SumTreeNameType, + who: AccountIdOf, + vote_commit: [u8; 32], + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Commit, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + let drawn_jurors = >::get(&key); + match drawn_jurors.binary_search_by(|(c, _)| c.cmp(&who.clone())) { + Ok(_) => { + let vote_commit_struct = ScoreCommitVote { + commit: vote_commit, + votestatus: VoteStatus::Commited, + revealed_vote: None, + }; + >::insert(&key, &who, vote_commit_struct); + } + Err(_) => Err(Error::::JurorDoesNotExists)?, + } + Ok(()) + } + + /// choice is i64. Validate the range of the choice while using the function + pub(super) fn reveal_vote_score_helper( + key: SumTreeNameType, + who: AccountIdOf, + choice: i64, + salt: Vec, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Vote, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + let who_commit_vote = >::get(&key, &who); + match who_commit_vote { + Some(mut commit_struct) => { + ensure!( + commit_struct.votestatus == VoteStatus::Commited, + Error::::VoteStatusNotCommited + ); + let mut vote = format!("{}", choice).as_bytes().to_vec(); + // let mut vote = choice.clone(); + let mut salt_a = salt.clone(); + vote.append(&mut salt_a); + let vote_bytes: &[u8] = &vote; + let hash = sp_io::hashing::keccak_256(vote_bytes); + let commit: &[u8] = &commit_struct.commit; + if hash == commit { + let mut reveal_score_values = >::get(&key); + reveal_score_values.push(choice * 1000); + >::insert(&key, reveal_score_values); + commit_struct.revealed_vote = Some(choice); + commit_struct.votestatus = VoteStatus::Revealed; + >::insert(&key, &who, commit_struct); + } else { + Err(Error::::CommitDoesNotMatch)? + } + } + None => Err(Error::::CommitDoesNotExists)?, + } + + Ok(()) + } + + /// Distribute incentives to all jurors in execution period in score schelling game + pub(super) fn get_incentives_score_schelling_helper( + key: SumTreeNameType, + phase_data: PhaseDataOf, + range_point: RangePoint, + ) -> DispatchResult { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Execution, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + + let drawn_jurors = >::get(&key); + let reveal_votes_iterator = >::iter_prefix(&key); + let reveal_values = >::get(&key); + let sd_and_mean = Self::std_deviation_interger(&reveal_values); + let new_mean = Self::calculate_new_mean(&reveal_values, sd_and_mean).unwrap(); + // println!("new mean: {:?}", new_mean); + >::insert(key.clone(), new_mean); + let incentives_range = Self::get_incentives_range(range_point); + let mut reveal_votes = reveal_votes_iterator + .map(|(account_id, score_commit_vote)| (account_id, score_commit_vote.revealed_vote)) + .collect::>(); + reveal_votes.sort_by(|a, b| a.0.cmp(&b.0)); + + // println!("reveal votes, {:?}",reveal_votes); + let mut winners = vec![]; + for juror in drawn_jurors { + match reveal_votes.binary_search_by(|(c, _)| c.cmp(&juror.0)) { + Ok(index) => { + // println!("Ok index {:?}", index); + let account_n_vote = &reveal_votes[index]; + if let Some(i) = account_n_vote.1 { + // println!("vote {:?}", i); + if i * 1000 >= new_mean.checked_sub(incentives_range).unwrap() + && i * 1000 <= new_mean.checked_add(incentives_range).unwrap() + { + // get incentives + winners.push((juror.0.clone(), juror.1.clone())); + } else { + // deduct incentives + let stake = juror.1; + let balance = Self::u64_to_balance_saturated(stake * 3 / 4); + let r = T::Currency::deposit_into_existing(&juror.0, balance) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + } + } + } + Err(_) => { + // println!("Err index {:?}", index); + } + } + } + + let winners_len = winners.len() as u64; + // println!("winners_len {}", winners_len); + let incentives_tuple = phase_data.juror_incentives; + let winning_incentives = incentives_tuple + .1 + .checked_div(winners_len) + .expect("oveflow"); + for winner in winners { + let total_incentives = winner.1.checked_add(winning_incentives).expect("overflow"); + let incentives = Self::u64_to_balance_saturated(total_incentives); + let r = T::Currency::deposit_into_existing(&winner.0, incentives) + .ok() + .unwrap(); + T::Reward::on_unbalanced(r); + } + + // Remove all data + + // Remove SorititionSumTrees in `sortition-sum-game` pallet + let _result = T::SortitionSumGameSource::remove_tree_link(key.clone()); + + // Remove DrawnJurors + >::remove(&key); + + // Remove UnstakedJurors (all jurors can be returned their incentives at a time) + + // Remove ScoreVoteCommits + >::remove_prefix(key.clone(), None); // Deprecated: Use clear_prefix instead + // let reveal_votes_iterator2 = >::iter_prefix(&key); + // reveal_votes_iterator2.for_each(|(account_id, _)|{ + // >::remove(key.clone(), account_id); + // }); + + // Remove RevealScoreValues + >::remove(&key); + + Ok(()) + } + + pub(super) fn get_result_of_juror_score( + key: SumTreeNameType, + who: AccountIdOf, + range_point: RangePoint, + ) -> Result { + match >::get(&key) { + Some(period) => { + ensure!(period == Period::Execution, Error::::PeriodDontMatch); + } + None => Err(Error::::PeriodDoesNotExists)?, + } + let new_mean = Self::get_mean_value(key.clone())?; + + let incentives_range = Self::get_incentives_range(range_point); + let reveal_votes = >::get(&key, &who); + match reveal_votes { + Some(commit_struct) => { + let vote_option = commit_struct.revealed_vote; + match vote_option { + Some(vote) => { + if vote * 1000 >= new_mean.checked_sub(incentives_range).unwrap() + && vote * 1000 <= new_mean.checked_add(incentives_range).unwrap() + { + // get incentives + Ok(JurorGameResult::Won) + } else { + Ok(JurorGameResult::Lost) + } + } + None => Err(Error::::VoteNotRevealed)?, + } + } + None => Err(Error::::CommitDoesNotExists)?, + } + } + + pub(super) fn set_new_mean_value(key: SumTreeNameType) -> DispatchResult { + let reveal_values = >::get(&key); + let sd_and_mean = Self::std_deviation_interger(&reveal_values); + let new_mean = Self::calculate_new_mean(&reveal_values, sd_and_mean).unwrap(); + // println!("new mean: {:?}", new_mean); + >::insert(key.clone(), new_mean); + + Ok(()) + } + + pub(super) fn get_mean_value(key: SumTreeNameType) -> Result { + let mean_option = >::get(key); + match mean_option { + Some(mean) => Ok(mean), + None => Err(Error::::NewMeanNotInserted)?, + } + } + + /// Calculate the mean of integer + pub(super) fn mean_integer(data: &Vec) -> Option { + let data_mul_sum = data.iter().sum::(); + let count = data.len(); + + match count { + positive if positive > 0 => Some(data_mul_sum / count as i64), + _ => None, + } + } + + pub(super) fn std_deviation_interger(data: &Vec) -> Option<(i64, i64)> { + let mean = Self::mean_integer(data); + match (mean, data.len()) { + (Some(data_mean), count) if count > 0 => { + let variance = data + .iter() + .map(|value| { + let diff = data_mean.checked_sub(*value as i64).unwrap(); + diff * diff + }) + .sum::() + / count as i64; + + Some((variance.sqrt(), mean.unwrap())) + } + _ => None, + } + } + + pub(super) fn calculate_new_mean( + data: &Vec, + sd_and_mean: Option<(i64, i64)>, + ) -> Option { + let mut new_items = vec![]; + let mean = sd_and_mean.unwrap().1; + let sd = sd_and_mean.unwrap().0; + for x in data { + if *x >= mean.checked_sub(sd).unwrap() && *x <= mean.checked_add(sd).unwrap() { + new_items.push(*x); + } + } + let new_mean = Self::mean_integer(&new_items); + new_mean + } + + pub(super) fn get_incentives_range(range_point: RangePoint) -> i64 { + match range_point { + RangePoint::ZeroToTen => 1500, //3 points, 1.5 ± mean, multiply by 1000 to make it integer + RangePoint::MinusTenToPlusTen => 3000, //6 points, 3 ± mean + RangePoint::ZeroToFive => 750, //1.5 points, 0.75 ± mean + } + } +} diff --git a/custom-pallets/schelling-game-shared/src/share_link.rs b/custom-pallets/schelling-game-shared/src/share_link.rs new file mode 100644 index 0000000..935ccf9 --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/share_link.rs @@ -0,0 +1,332 @@ +use crate::*; + +use trait_schelling_game_shared::SchellingGameSharedLink; + +impl SchellingGameSharedLink for Pallet { + type SumTreeName = SumTreeNameType; + type SchellingGameType = SchellingGameType; + type BlockNumber = BlockNumberOf; + type AccountId = AccountIdOf; + type Balance = BalanceOf; + type RangePoint = RangePoint; + type Period = Period; + type PhaseData = PhaseDataOf; + type WinningDecision = WinningDecision; + type JurorGameResult = JurorGameResult; + + fn create_phase_data( + block_length: u64, + max_draws: u64, + min_number_juror_staked: u64, + min_juror_stake: u64, + juror_incentives: (u64, u64), + ) -> Self::PhaseData { + Self::create_phase_data( + block_length, + max_draws, + min_number_juror_staked, + min_juror_stake, + juror_incentives, + ) + } + + fn create_phase_with_all_data( + evidence_length: u64, + end_of_staking_time: u64, + staking_length: u64, + drawing_length: u64, + commit_length: u64, + vote_length: u64, + appeal_length: u64, + max_draws: u64, + min_number_juror_staked: u64, + min_juror_stake: u64, + juror_incentives: (u64, u64), + ) -> Self::PhaseData { + Self::create_phase_with_all_data( + evidence_length, + end_of_staking_time, + staking_length, + drawing_length, + commit_length, + vote_length, + appeal_length, + max_draws, + min_number_juror_staked, + min_juror_stake, + juror_incentives, + ) + } + + /// Get the Period + fn get_period_link(key: Self::SumTreeName) -> Option { + Self::get_period(key) + } + + /// Set `PeriodName` to `Period::Evidence` + /// Called with submission of `Evidence` stake e.g. Profile stake + /// Also set `EvidenceStartTime` + fn set_to_evidence_period_link( + key: Self::SumTreeName, + now: Self::BlockNumber, + ) -> DispatchResult { + Self::set_to_evidence_period(key, now) + } + + /// Create a sortition sum tree + fn create_tree_helper_link(key: Self::SumTreeName, k: u64) -> DispatchResult { + Self::create_tree_link_helper(key, k) + } + + /// Check `Period` is `Evidence`, and change it to `Staking` + /// It is called with function that submits challenge stake after `end_block` of evidence period + /// Checks evidence period is over + #[doc=include_str!("docimage/set_to_staking_period_1.svg")] + /// ```ignore + /// if time >= block_time.min_short_block_length { + /// // change `Period` to `Staking` + /// } + /// ``` + fn set_to_staking_period_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + now: Self::BlockNumber, + ) -> DispatchResult { + Self::set_to_staking_period(key, phase_data, now) + } + + fn ensure_time_for_staking_over_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + now: Self::BlockNumber, + ) -> DispatchResult { + Self::ensure_time_for_staking_over(key, phase_data, now) + } + + fn set_to_staking_period_pe_link( + key: Self::SumTreeName, + now: Self::BlockNumber, + ) -> DispatchResult { + Self::set_to_staking_period_pe(key, now) + } + + /// Change the `Period` + /// + /// `Period::Staking` to `Period::Drawing` + #[doc=include_str!("docimage/change_period_link_1.svg")] + /// ```ignore + /// if now >= min_long_block_length + staking_start_time { + /// // Change `Period::Staking` to `Period::Drawing` + /// } + /// ``` + /// + /// `Period::Drawing` to `Period::Commit` + /// When maximum juror are drawn + /// + /// `Period::Commit` to `Period::Vote` + /// ```ignore + /// if now >= min_long_block_length + commit_start_time { + /// // Change `Period::Commit` to `Period::Vote` + /// } + /// ``` + /// + /// `Period::Vote` to `Period::Execution` + /// ```ignore + /// if now >= min_long_block_length + vote_start_time { + /// // Change `Period::Vote` to `Period::Execution` + /// } + /// ``` + fn change_period_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + now: Self::BlockNumber, + ) -> DispatchResult { + Self::change_period(key, phase_data, now) + } + + /// Apply Jurors + /// Ensure `Period` is `Staking` + /// Slash the stake. + /// Store the stake on sortition sum tree if doesn't exists. + fn apply_jurors_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + who: Self::AccountId, + stake: Self::Balance, + ) -> DispatchResult { + Self::apply_jurors_helper(key, phase_data, who, stake) + } + + /// Draw Jurors + /// Ensure `Period` is `Drawing` + /// `iterations` is number of jurors drawn per call + /// Ensure total draws `draws_in_round` is less than `max_draws` + fn draw_jurors_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + iterations: u64, + ) -> DispatchResult { + Self::draw_jurors_helper(key, phase_data, iterations) + } + + /// Unstake those who are not drawn as jurors + /// They can withdraw their stake + fn unstaking_helper_link(key: Self::SumTreeName, who: Self::AccountId) -> DispatchResult { + Self::unstaking_helper(key, who) + } + + /// Commit vote + fn commit_vote_helper_link( + key: Self::SumTreeName, + who: Self::AccountId, + vote_commit: [u8; 32], + ) -> DispatchResult { + Self::commit_vote_helper(key, who, vote_commit) + } + + /// Reveal vote + /// There are two vote choices 0 or 1 + fn reveal_vote_two_choice_helper_link( + key: Self::SumTreeName, + who: Self::AccountId, + choice: u128, + salt: Vec, + ) -> DispatchResult { + Self::reveal_vote_two_choice_helper(key, who, choice, salt) + } + /// Distribute incentives for two choices + /// Winner gets `stake` + `winning_incentives` + /// If decision is draw, jurors receive their `stake` + /// Lost jurors gets `stake * 3/4` + /// When they receive their incentives, their accountid is stored in `JurorsIncentiveDistributedAccounts` + fn get_incentives_two_choice_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + who: Self::AccountId, + ) -> DispatchResult { + Self::get_incentives_two_choice_helper(key, phase_data, who) + } + + /// Blocks left for ending evidence period + /// When evidence time ends, you can submit the challenge stake + /// `start_block_number` evidence start time which you will get from `EvidenceStartTime` + fn get_evidence_period_end_block_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + now: Self::BlockNumber, + ) -> Option { + Self::get_evidence_period_end_block_helper(key, phase_data, now) + } + + /// Blocks left for ending staking period + fn get_staking_period_end_block_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + now: Self::BlockNumber, + ) -> Option { + Self::get_staking_period_end_block_helper(key, phase_data, now) + } + + /// Return true when drawing period is over, otherwise false + fn get_drawing_period_end_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + ) -> (u64, u64, bool) { + Self::get_drawing_period_end_helper(key, phase_data) + } + + /// Blocks left for ending drawing period + fn get_commit_period_end_block_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + now: Self::BlockNumber, + ) -> Option { + Self::get_commit_period_end_block_helper(key, phase_data, now) + } + + /// Blocks left for ending vote period + fn get_vote_period_end_block_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + now: Self::BlockNumber, + ) -> Option { + Self::get_vote_period_end_block_helper(key, phase_data, now) + } + + /// Check if `AccountId` is selected as juror + fn selected_as_juror_helper_link(key: Self::SumTreeName, who: Self::AccountId) -> bool { + Self::selected_as_juror_helper(key, who) + } + + /// Commit vote for score schelling game + fn commit_vote_for_score_helper_link( + key: Self::SumTreeName, + who: Self::AccountId, + vote_commit: [u8; 32], + ) -> DispatchResult { + Self::commit_vote_for_score_helper(key, who, vote_commit) + } + + /// Reveal vote for score schelling game + fn reveal_vote_score_helper_link( + key: Self::SumTreeName, + who: Self::AccountId, + choice: i64, + salt: Vec, + ) -> DispatchResult { + Self::reveal_vote_score_helper(key, who, choice, salt) + } + + /// Distribute incentives to all score schelling game jurors + fn get_incentives_score_schelling_helper_link( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + range_point: Self::RangePoint, + ) -> DispatchResult { + Self::get_incentives_score_schelling_helper(key, phase_data, range_point) + } + + /// Get new mean in score schelling game + fn get_mean_value_link(key: Self::SumTreeName) -> Result { + Self::get_mean_value(key) + } + + /// Distribute incentives to all two choice shelling game jurors + fn get_all_incentives_two_choice_helper( + key: Self::SumTreeName, + phase_data: Self::PhaseData, + ) -> DispatchResult { + Self::get_all_incentives_two_choice_helper(key, phase_data) + } + + fn get_drawn_jurors(key: Self::SumTreeName) -> Vec<(Self::AccountId, u64)> { + Self::drawn_jurors(key) + } + + fn get_winning_decision_value_link(key: Self::SumTreeName) -> WinningDecision { + Self::get_winning_decision_value(key) + } + + fn get_result_of_juror( + key: Self::SumTreeName, + who: Self::AccountId, + ) -> Result<(JurorGameResult, u64), DispatchError> { + Self::get_result_of_juror(key, who) + } + + fn get_result_of_juror_score( + key: Self::SumTreeName, + who: Self::AccountId, + range_point: Self::RangePoint, + ) -> Result { + Self::get_result_of_juror_score(key, who, range_point) + } + + fn set_new_mean_value(key: Self::SumTreeName) -> DispatchResult { + Self::set_new_mean_value(key) + } + + fn add_to_incentives_count(key: Self::SumTreeName, who: Self::AccountId) -> DispatchResult { + Self::add_to_incentives_count(key, who) + } +} diff --git a/custom-pallets/schelling-game-shared/src/tests.rs b/custom-pallets/schelling-game-shared/src/tests.rs new file mode 100644 index 0000000..e11cd52 --- /dev/null +++ b/custom-pallets/schelling-game-shared/src/tests.rs @@ -0,0 +1,1168 @@ +use crate::{ + mock::*, + types::{JurorGameResult, Period, PhaseData, RangePoint, SchellingGameType}, + Error, Event, +}; +use frame_support::{assert_noop, assert_ok}; + +use pallet_sortition_sum_game::types::SumTreeName; + +type CitizenId = u64; + +fn return_key_profile(citizen_id: CitizenId) -> SumTreeName { + let key = SumTreeName::ProfileValidation { + citizen_address: citizen_id, + block_number: 10, + }; + key +} + +fn return_game_type_profile_approval() -> SchellingGameType { + SchellingGameType::ProfileApproval +} + +fn get_the_phase_data() -> PhaseData { + let data = PhaseData::create_with_data(50, 5, 3, 100, (100, 100)); + data +} + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + // Dispatch a signed extrinsic. + assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); + // Read pallet storage and assert an expected result. + assert_eq!(TemplateModule::something(), Some(42)); + // Assert that the correct event was deposited + System::assert_last_event( + Event::SomethingStored { + something: 42, + who: 1, + } + .into(), + ); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the expected error is thrown when no value is present. + assert_noop!( + TemplateModule::cause_error(RuntimeOrigin::signed(1)), + Error::::NoneValue + ); + }); +} + +#[test] +fn evidence_period_not_over_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let phase_data = get_the_phase_data(); + let now2 = now + phase_data.evidence_length - 1; + assert_noop!( + TemplateModule::set_to_staking_period(key.clone(), phase_data, now2), + Error::::EvidencePeriodNotOver + ); + }); +} + +/// 1) Set evidence period +/// 2) Set staking period +/// 3) Create tree +#[test] +fn evidence_period_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let phase_data = get_the_phase_data(); + let now2 = now + phase_data.evidence_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data, + now2 + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + }); +} + +/// End of staking period + +#[test] +fn end_of_time_staking_period() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let phase_data = get_the_phase_data(); + let now2 = now + phase_data.evidence_length + phase_data.end_of_staking_time - 1; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data, + now2 + )); + let phase_data = get_the_phase_data(); + let now2 = now + phase_data.evidence_length + phase_data.end_of_staking_time; + assert_noop!( + TemplateModule::set_to_staking_period(key.clone(), phase_data, now2), + Error::::PeriodIsNotEvidence + ); + }); +} + +/// Check time for staking over +#[test] +fn check_time_for_staking_not_over_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let phase_data = get_the_phase_data(); + let now2 = now + phase_data.evidence_length + phase_data.end_of_staking_time - 1; + assert_noop!( + TemplateModule::ensure_time_for_staking_over(key.clone(), phase_data, now2), + Error::::TimeForStakingNotOver + ); + let phase_data = get_the_phase_data(); + let now = now + phase_data.evidence_length + phase_data.end_of_staking_time; + assert_ok!(TemplateModule::ensure_time_for_staking_over( + key.clone(), + phase_data, + now + )); + }); +} + +#[test] +fn apply_juror() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let phase_data = get_the_phase_data(); + let now2 = now + phase_data.evidence_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data.clone(), + now2 + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + // Check the period is staking + let period = TemplateModule::get_period(key.clone()); + // println!("{:?}", period); + assert_eq!(Some(Period::Staking), period); + // Applyjuror + for j in 4..30 { + assert_ok!(TemplateModule::apply_jurors_helper( + key.clone(), + phase_data.clone(), + j, + j * 100 + )); + } + }); +} + +#[test] +fn challenger_win_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let phase_data = get_the_phase_data(); + + let staking_start_time = now + phase_data.evidence_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data.clone(), + staking_start_time + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + // Check the period is staking + let period = TemplateModule::get_period(key.clone()); + // println!("{:?}", period); + assert_eq!(Some(Period::Staking), period); + // Applyjuror + for j in 4..30 { + assert_ok!(TemplateModule::apply_jurors_helper( + key.clone(), + phase_data.clone(), + j, + j * 100 + )); + } + let new_now = staking_start_time + phase_data.staking_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Drawing), period); + assert_ok!(TemplateModule::draw_jurors_helper( + key.clone(), + phase_data.clone(), + 5 + )); + let draws_in_round = TemplateModule::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let balance = Balances::free_balance(5); + assert_eq!(299500, balance); + assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); + let balance = Balances::free_balance(5); + assert_eq!(300000, balance); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 4, hash)); + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 7, hash)); + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 13, hash)); + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 14, hash)); + let hash = sp_io::hashing::keccak_256("0salt5".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 15, hash)); + let commit_start_time = TemplateModule::commit_start_time(key.clone()); + let new_now = commit_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Vote), period); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 4, + 1, + "salt".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 7, + 1, + "salt2".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 13, + 1, + "salt3".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 14, + 1, + "salt4".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 15, + 0, + "salt5".as_bytes().to_vec() + )); + let decision = TemplateModule::decision_count(key.clone()); + assert_eq!((1, 4), decision); + let vote_start_time = TemplateModule::vote_start_time(key.clone()); + let new_now = vote_start_time + phase_data.vote_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Execution), period); + + let balance = Balances::free_balance(4); + assert_eq!(299600, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 4 + )); + let balance = Balances::free_balance(4); + assert_eq!(300025, balance); + let balance = Balances::free_balance(7); + // println!("{:?}", balance); + assert_eq!(299300, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 7 + )); + let balance = Balances::free_balance(7); + assert_eq!(300025, balance); + let balance = Balances::free_balance(13); + assert_eq!(298700, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 13 + )); + let balance = Balances::free_balance(13); + assert_eq!(300025, balance); + let balance = Balances::free_balance(14); + assert_eq!(298600, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 14 + )); + let balance = Balances::free_balance(14); + assert_eq!(300025, balance); + let balance = Balances::free_balance(15); + assert_eq!(298500, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 15 + )); + let balance = Balances::free_balance(15); + assert_eq!(299625, balance); + }); +} + +#[test] +fn challenger_win_value_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let phase_data = get_the_phase_data(); + + let staking_start_time = now + phase_data.evidence_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data.clone(), + staking_start_time + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + // Check the period is staking + let period = TemplateModule::get_period(key.clone()); + // println!("{:?}", period); + assert_eq!(Some(Period::Staking), period); + // Applyjuror + for j in 4..30 { + assert_ok!(TemplateModule::apply_jurors_helper( + key.clone(), + phase_data.clone(), + j, + j * 100 + )); + } + let new_now = staking_start_time + phase_data.staking_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Drawing), period); + assert_ok!(TemplateModule::draw_jurors_helper( + key.clone(), + phase_data.clone(), + 5 + )); + let draws_in_round = TemplateModule::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let balance = Balances::free_balance(5); + assert_eq!(299500, balance); + assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); + let balance = Balances::free_balance(5); + assert_eq!(300000, balance); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 4, hash)); + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 7, hash)); + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 13, hash)); + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 14, hash)); + let hash = sp_io::hashing::keccak_256("0salt5".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 15, hash)); + let commit_start_time = TemplateModule::commit_start_time(key.clone()); + let new_now = commit_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Vote), period); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 4, + 1, + "salt".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 7, + 1, + "salt2".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 13, + 1, + "salt3".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 14, + 1, + "salt4".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 15, + 0, + "salt5".as_bytes().to_vec() + )); + let decision = TemplateModule::decision_count(key.clone()); + assert_eq!((1, 4), decision); + let vote_start_time = TemplateModule::vote_start_time(key.clone()); + let new_now = vote_start_time + phase_data.vote_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Execution), period); + let result_stake = TemplateModule::get_result_of_juror(key.clone(), 4); + let (result, _) = result_stake.unwrap(); + assert_eq!(result, JurorGameResult::Won); + let result_stake = TemplateModule::get_result_of_juror(key.clone(), 7); + let (result, _) = result_stake.unwrap(); + assert_eq!(result, JurorGameResult::Won); + let result_stake = TemplateModule::get_result_of_juror(key.clone(), 13); + let (result, _) = result_stake.unwrap(); + assert_eq!(result, JurorGameResult::Won); + let result_stake = TemplateModule::get_result_of_juror(key.clone(), 14); + let (result, _) = result_stake.unwrap(); + assert_eq!(result, JurorGameResult::Won); + let result_stake = TemplateModule::get_result_of_juror(key.clone(), 15); + let (result, _) = result_stake.unwrap(); + assert_eq!(result, JurorGameResult::Lost); + }); +} + +#[test] +fn challenger_win_test_jurors_incentive_in_one_go() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let game_type = return_game_type_profile_approval(); + + // let min_short_block_length = return_min_short_block_length(); + // let min_long_block_length = return_min_long_block_length(); + + let phase_data = get_the_phase_data(); + + let staking_start_time = now + phase_data.staking_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data.clone(), + staking_start_time + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + // Check the period is staking + let period = TemplateModule::get_period(key.clone()); + // println!("{:?}", period); + assert_eq!(Some(Period::Staking), period); + // Applyjuror + for j in 4..30 { + assert_ok!(TemplateModule::apply_jurors_helper( + key.clone(), + phase_data.clone(), + j, + j * 100 + )); + } + let new_now = staking_start_time + phase_data.staking_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Drawing), period); + assert_ok!(TemplateModule::draw_jurors_helper( + key.clone(), + phase_data.clone(), + 5 + )); + let draws_in_round = TemplateModule::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let balance = Balances::free_balance(5); + assert_eq!(299500, balance); + assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); + let balance = Balances::free_balance(5); + assert_eq!(300000, balance); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 4, hash)); + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 7, hash)); + let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 13, hash)); + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 14, hash)); + let hash = sp_io::hashing::keccak_256("0salt5".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 15, hash)); + let commit_start_time = TemplateModule::commit_start_time(key.clone()); + let new_now = commit_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Vote), period); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 4, + 1, + "salt".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 7, + 1, + "salt2".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 13, + 1, + "salt3".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 14, + 1, + "salt4".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 15, + 0, + "salt5".as_bytes().to_vec() + )); + let decision = TemplateModule::decision_count(key.clone()); + assert_eq!((1, 4), decision); + let vote_start_time = TemplateModule::vote_start_time(key.clone()); + let new_now = vote_start_time + phase_data.vote_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Execution), period); + let balance = Balances::free_balance(4); + assert_eq!(299600, balance); + let balance = Balances::free_balance(7); + // println!("{:?}", balance); + assert_eq!(299300, balance); + let balance = Balances::free_balance(13); + assert_eq!(298700, balance); + let balance = Balances::free_balance(14); + assert_eq!(298600, balance); + let balance = Balances::free_balance(15); + assert_eq!(298500, balance); + assert_ok!(TemplateModule::get_all_incentives_two_choice_helper( + key.clone(), + phase_data.clone() + )); + let balance = Balances::free_balance(4); + assert_eq!(300025, balance); + let balance = Balances::free_balance(7); + assert_eq!(300025, balance); + let balance = Balances::free_balance(13); + assert_eq!(300025, balance); + let balance = Balances::free_balance(14); + assert_eq!(300025, balance); + let balance = Balances::free_balance(15); + assert_eq!(299625, balance); + }); +} + +#[test] +fn challenger_lost_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + // let game_type = return_game_type_profile_approval(); + let phase_data = get_the_phase_data(); + + // let min_short_block_length = return_min_short_block_length(); + // let min_long_block_length = return_min_long_block_length(); + let staking_start_time = now + phase_data.staking_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data.clone(), + staking_start_time + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + // Check the period is staking + let period = TemplateModule::get_period(key.clone()); + // println!("{:?}", period); + assert_eq!(Some(Period::Staking), period); + // Applyjuror + for j in 4..30 { + assert_ok!(TemplateModule::apply_jurors_helper( + key.clone(), + phase_data.clone(), + j, + j * 100 + )); + } + let new_now = staking_start_time + phase_data.staking_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Drawing), period); + assert_ok!(TemplateModule::draw_jurors_helper( + key.clone(), + phase_data.clone(), + 5 + )); + let draws_in_round = TemplateModule::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let balance = Balances::free_balance(5); + assert_eq!(299500, balance); + assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); + let balance = Balances::free_balance(5); + assert_eq!(300000, balance); + let hash = sp_io::hashing::keccak_256("0salt".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 4, hash)); + let hash = sp_io::hashing::keccak_256("0salt2".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 7, hash)); + let hash = sp_io::hashing::keccak_256("0salt3".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 13, hash)); + let hash = sp_io::hashing::keccak_256("0salt4".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 14, hash)); + let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); + assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 15, hash)); + let commit_start_time = TemplateModule::commit_start_time(key.clone()); + let new_now = commit_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Vote), period); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 4, + 0, + "salt".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 7, + 0, + "salt2".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 13, + 0, + "salt3".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 14, + 0, + "salt4".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_two_choice_helper( + key.clone(), + 15, + 1, + "salt5".as_bytes().to_vec() + )); + let decision = TemplateModule::decision_count(key.clone()); + assert_eq!((4, 1), decision); + let vote_start_time = TemplateModule::vote_start_time(key.clone()); + let new_now = vote_start_time + phase_data.vote_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Execution), period); + + let balance = Balances::free_balance(4); + assert_eq!(299600, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 4 + )); + let balance = Balances::free_balance(4); + assert_eq!(300025, balance); + let balance = Balances::free_balance(7); + // println!("{:?}", balance); + assert_eq!(299300, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 7 + )); + let balance = Balances::free_balance(7); + assert_eq!(300025, balance); + let balance = Balances::free_balance(13); + assert_eq!(298700, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 13 + )); + let balance = Balances::free_balance(13); + assert_eq!(300025, balance); + let balance = Balances::free_balance(14); + assert_eq!(298600, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 14 + )); + let balance = Balances::free_balance(14); + assert_eq!(300025, balance); + let balance = Balances::free_balance(15); + assert_eq!(298500, balance); + assert_ok!(TemplateModule::get_incentives_two_choice_helper( + key.clone(), + phase_data.clone(), + 15 + )); + let balance = Balances::free_balance(15); + assert_eq!(299625, balance); + }); +} + +#[test] +fn score_schelling_game_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let game_type = return_game_type_profile_approval(); + // let min_short_block_length = return_min_short_block_length(); + // let min_long_block_length = return_min_long_block_length(); + let phase_data = get_the_phase_data(); + + let staking_start_time = now + phase_data.staking_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data.clone(), + staking_start_time + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + // Check the period is staking + let period = TemplateModule::get_period(key.clone()); + // println!("{:?}", period); + assert_eq!(Some(Period::Staking), period); + // Applyjuror + for j in 4..30 { + assert_ok!(TemplateModule::apply_jurors_helper( + key.clone(), + phase_data.clone(), + j, + j * 100 + )); + } + let new_now = staking_start_time + phase_data.staking_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Drawing), period); + assert_ok!(TemplateModule::draw_jurors_helper( + key.clone(), + phase_data.clone(), + 5 + )); + let draws_in_round = TemplateModule::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let balance = Balances::free_balance(5); + assert_eq!(299500, balance); + assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); + let balance = Balances::free_balance(5); + assert_eq!(300000, balance); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 4, + hash + )); + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 7, + hash + )); + let hash = sp_io::hashing::keccak_256("5salt3".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 13, + hash + )); + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 14, + hash + )); + let hash = sp_io::hashing::keccak_256("7salt5".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 15, + hash + )); + let commit_start_time = TemplateModule::commit_start_time(key.clone()); + let new_now = commit_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Vote), period); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 4, + 1, + "salt".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 7, + 1, + "salt2".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 13, + 5, + "salt3".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 14, + 1, + "salt4".as_bytes().to_vec() + )); + assert_noop!( + TemplateModule::reveal_vote_score_helper( + key.clone(), + 15, + 8, + "salt5".as_bytes().to_vec() + ), + Error::::CommitDoesNotMatch + ); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 15, + 7, + "salt5".as_bytes().to_vec() + )); + let vote_start_time = TemplateModule::vote_start_time(key.clone()); + let new_now = vote_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Execution), period); + let reveal_score = TemplateModule::reveal_score_values(key.clone()); + assert_eq!(vec![1000, 1000, 5000, 1000, 7000], reveal_score); + let balance = Balances::free_balance(4); + assert_eq!(299600, balance); + let balance = Balances::free_balance(7); + // println!("{:?}", balance); + assert_eq!(299300, balance); + let balance = Balances::free_balance(13); + assert_eq!(298700, balance); + let balance = Balances::free_balance(14); + assert_eq!(298600, balance); + let balance = Balances::free_balance(15); + assert_eq!(298500, balance); + assert_ok!(TemplateModule::get_incentives_score_schelling_helper( + key.clone(), + phase_data.clone(), + RangePoint::ZeroToTen + )); + let mean_values = TemplateModule::new_mean_reveal_score(key.clone()); + assert_eq!(2000, mean_values.unwrap()); + let balance = Balances::free_balance(4); + // println!("{:?}", balance); + assert_eq!(300033, balance); + let balance = Balances::free_balance(7); + assert_eq!(300033, balance); + let balance = Balances::free_balance(13); // Balance deducted as voted 5 + assert_eq!(299675, balance); + let balance = Balances::free_balance(14); + assert_eq!(300033, balance); + let balance = Balances::free_balance(15); // Balance deducted as voted 7 + assert_eq!(299625, balance); + }); +} + +#[test] +fn score_schelling_game_value_test() { + new_test_ext().execute_with(|| { + let key = return_key_profile(0); + let now = 10; + assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); + assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); + let game_type = return_game_type_profile_approval(); + // let min_short_block_length = return_min_short_block_length(); + // let min_long_block_length = return_min_long_block_length(); + let phase_data = get_the_phase_data(); + + let staking_start_time = now + phase_data.staking_length; + assert_ok!(TemplateModule::set_to_staking_period( + key.clone(), + phase_data.clone(), + staking_start_time + )); + // Create tree + assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); + // Check the period is staking + let period = TemplateModule::get_period(key.clone()); + // println!("{:?}", period); + assert_eq!(Some(Period::Staking), period); + // Applyjuror + for j in 4..30 { + assert_ok!(TemplateModule::apply_jurors_helper( + key.clone(), + phase_data.clone(), + j, + j * 100 + )); + } + let new_now = staking_start_time + phase_data.staking_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Drawing), period); + assert_ok!(TemplateModule::draw_jurors_helper( + key.clone(), + phase_data.clone(), + 5 + )); + let draws_in_round = TemplateModule::draws_in_round(key.clone()); + assert_eq!(5, draws_in_round); + let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); + assert_eq!( + vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], + drawn_jurors + ); + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let balance = Balances::free_balance(5); + assert_eq!(299500, balance); + assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); + let balance = Balances::free_balance(5); + assert_eq!(300000, balance); + let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 4, + hash + )); + let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 7, + hash + )); + let hash = sp_io::hashing::keccak_256("5salt3".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 13, + hash + )); + let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 14, + hash + )); + let hash = sp_io::hashing::keccak_256("7salt5".as_bytes()); + assert_ok!(TemplateModule::commit_vote_for_score_helper( + key.clone(), + 15, + hash + )); + let commit_start_time = TemplateModule::commit_start_time(key.clone()); + let new_now = commit_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Vote), period); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 4, + 1, + "salt".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 7, + 1, + "salt2".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 13, + 5, + "salt3".as_bytes().to_vec() + )); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 14, + 1, + "salt4".as_bytes().to_vec() + )); + assert_noop!( + TemplateModule::reveal_vote_score_helper( + key.clone(), + 15, + 8, + "salt5".as_bytes().to_vec() + ), + Error::::CommitDoesNotMatch + ); + assert_ok!(TemplateModule::reveal_vote_score_helper( + key.clone(), + 15, + 7, + "salt5".as_bytes().to_vec() + )); + let vote_start_time = TemplateModule::vote_start_time(key.clone()); + let new_now = vote_start_time + phase_data.commit_length; + assert_ok!(TemplateModule::change_period( + key.clone(), + phase_data.clone(), + new_now.clone() + )); + let period = TemplateModule::get_period(key.clone()); + assert_eq!(Some(Period::Execution), period); + let reveal_score = TemplateModule::reveal_score_values(key.clone()); + assert_eq!(vec![1000, 1000, 5000, 1000, 7000], reveal_score); + assert_ok!(TemplateModule::set_new_mean_value(key.clone())); + let mean_values = TemplateModule::new_mean_reveal_score(key.clone()); + assert_eq!(2000, mean_values.unwrap()); + let result = + TemplateModule::get_result_of_juror_score(key.clone(), 4, RangePoint::ZeroToTen); + assert_eq!(result.unwrap(), JurorGameResult::Won); + let result = + TemplateModule::get_result_of_juror_score(key.clone(), 7, RangePoint::ZeroToTen); + assert_eq!(result.unwrap(), JurorGameResult::Won); + let result = + TemplateModule::get_result_of_juror_score(key.clone(), 13, RangePoint::ZeroToTen); + assert_eq!(result.unwrap(), JurorGameResult::Lost); + let result = + TemplateModule::get_result_of_juror_score(key.clone(), 14, RangePoint::ZeroToTen); + assert_eq!(result.unwrap(), JurorGameResult::Won); + let result = + TemplateModule::get_result_of_juror_score(key.clone(), 15, RangePoint::ZeroToTen); + assert_eq!(result.unwrap(), JurorGameResult::Lost); + }); +} diff --git a/pallets/schelling-game-shared/src/types.rs b/custom-pallets/schelling-game-shared/src/types.rs similarity index 56% rename from pallets/schelling-game-shared/src/types.rs rename to custom-pallets/schelling-game-shared/src/types.rs index 3dbfa85..f8e3131 100644 --- a/pallets/schelling-game-shared/src/types.rs +++ b/custom-pallets/schelling-game-shared/src/types.rs @@ -1,45 +1,45 @@ use super::*; use frame_support::pallet_prelude::*; -use frame_support::sp_std::prelude::*; use scale_info::TypeInfo; +use sp_std::prelude::*; #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub enum Period { - Evidence, // Evidence can be submitted. This is also when drawing has to take place. - Staking, // Stake sum trees can be updated. Pass after `minStakingTime` passes and there is at least one dispute without jurors. - Drawing, // Jurors can be drawn. Pass after all disputes have jurors or `maxDrawingTime` passes. - Commit, // Jurors commit a hashed vote. This is skipped for courts without hidden votes. - Vote, // Jurors reveal/cast their vote depending on whether the court has hidden votes or not. - Appeal, // The dispute can be appealed. - Execution, // Tokens are redistributed and the ruling is executed. + Evidence, // Evidence can be submitted. This is also when drawing has to take place. + Staking, // Stake sum trees can be updated. Pass after `minStakingTime` passes and there is at least one dispute without jurors. + Drawing, // Jurors can be drawn. Pass after all disputes have jurors or `maxDrawingTime` passes. + Commit, // Jurors commit a hashed vote. This is skipped for courts without hidden votes. + Vote, // Jurors reveal/cast their vote depending on whether the court has hidden votes or not. + Appeal, // The dispute can be appealed. + Execution, // Tokens are redistributed and the ruling is executed. } #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub enum SchellingGameType { - ProfileApproval, - ProfileScore, - ProjectReview, - PriceDiscovery, - PositiveExternality, - DepartmentScore, + ProfileApproval, + ProfileScore, + ProjectReview, + PriceDiscovery, + PositiveExternality, + DepartmentScore, } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct PhaseData { - pub evidence_length: T::BlockNumber, - pub end_of_staking_time: T::BlockNumber, - pub staking_length: T::BlockNumber, - pub drawing_length: T::BlockNumber, - pub commit_length: T::BlockNumber, - pub vote_length: T::BlockNumber, - pub appeal_length: T::BlockNumber, - pub max_draws: u64, - pub min_number_juror_staked: u64, - pub min_juror_stake: BalanceOf, - pub juror_incentives: (u64, u64), // (looser burn, winner mint) + pub evidence_length: BlockNumberOf, + pub end_of_staking_time: BlockNumberOf, + pub staking_length: BlockNumberOf, + pub drawing_length: BlockNumberOf, + pub commit_length: BlockNumberOf, + pub vote_length: BlockNumberOf, + pub appeal_length: BlockNumberOf, + pub max_draws: u64, + pub min_number_juror_staked: u64, + pub min_juror_stake: BalanceOf, + pub juror_incentives: (u64, u64), // (looser burn, winner mint) } // #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -59,39 +59,47 @@ pub struct PhaseData { #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub enum VoteStatus { - Commited, - Revealed, + Commited, + Revealed, } #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub struct CommitVote { - pub commit: [u8; 32], - pub votestatus: VoteStatus, - pub revealed_vote: Option, + pub commit: [u8; 32], + pub votestatus: VoteStatus, + pub revealed_vote: Option, } #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub enum RevealedVote { - Yes, - No, + Yes, + No, } #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub enum WinningDecision { - WinnerYes, - WinnerNo, - Draw, + WinnerYes, + WinnerNo, + Draw, +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum JurorGameResult { + Won, + Lost, + Draw, } #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub struct ScoreCommitVote { - pub commit: [u8; 32], - pub votestatus: VoteStatus, - pub revealed_vote: Option, + pub commit: [u8; 32], + pub votestatus: VoteStatus, + pub revealed_vote: Option, } /// RangePoint enum to determine whether score values are from @@ -101,7 +109,7 @@ pub struct ScoreCommitVote { #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(Debug))] pub enum RangePoint { - ZeroToTen, - MinusTenToPlusTen, - ZeroToFive, + ZeroToTen, + MinusTenToPlusTen, + ZeroToFive, } diff --git a/pallets/project-tips/src/weights.rs b/custom-pallets/schelling-game-shared/src/weights.rs similarity index 100% rename from pallets/project-tips/src/weights.rs rename to custom-pallets/schelling-game-shared/src/weights.rs diff --git a/custom-pallets/shared-storage/Cargo.toml b/custom-pallets/shared-storage/Cargo.toml new file mode 100644 index 0000000..3fe3a0f --- /dev/null +++ b/custom-pallets/shared-storage/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "pallet-shared-storage" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true} +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +trait-shared-storage = { workspace = true } + + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/schelling-game-shared/README.md b/custom-pallets/shared-storage/README.md similarity index 100% rename from pallets/schelling-game-shared/README.md rename to custom-pallets/shared-storage/README.md diff --git a/custom-pallets/shared-storage/src/benchmarking.rs b/custom-pallets/shared-storage/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/shared-storage/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/shared-storage/src/extras.rs b/custom-pallets/shared-storage/src/extras.rs new file mode 100644 index 0000000..5e06f35 --- /dev/null +++ b/custom-pallets/shared-storage/src/extras.rs @@ -0,0 +1,40 @@ +use crate::*; + +use frame_support::pallet_prelude::DispatchResult; +use trait_shared_storage::SharedStorageLink; + +impl SharedStorageLink for Pallet { + type AccountId = AccountIdOf; + + fn check_citizen_is_approved_link(address: Self::AccountId) -> DispatchResult { + Self::check_citizen_is_approved(address) + } + fn get_approved_citizen_count_link() -> u64 { + Self::get_approved_citizen_count() + } + + fn set_positive_externality_link(address: Self::AccountId, score: i64) -> DispatchResult { + Self::set_positive_externality(address, score) + } +} + +impl Pallet { + pub(super) fn check_citizen_is_approved(address: T::AccountId) -> DispatchResult { + let members = ApprovedCitizenAddress::::get(); + + match members.binary_search(&address) { + Ok(_index) => Ok(()), + Err(_) => Err(Error::::CitizenNotApproved.into()), + } + } + + pub(super) fn get_approved_citizen_count() -> u64 { + let members = ApprovedCitizenAddress::::get(); + members.len() as u64 + } + + pub(super) fn set_positive_externality(address: T::AccountId, score: Score) -> DispatchResult { + PositiveExternalityScore::::insert(address, score); + Ok(()) + } +} diff --git a/custom-pallets/shared-storage/src/lib.rs b/custom-pallets/shared-storage/src/lib.rs new file mode 100644 index 0000000..9ad2c6c --- /dev/null +++ b/custom-pallets/shared-storage/src/lib.rs @@ -0,0 +1,149 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; +mod extras; + +use frame_support::sp_runtime; +use frame_support::traits::BuildGenesisConfig; +use frame_system::pallet_prelude::*; +use sp_std::prelude::*; + +type AccountIdOf = ::AccountId; +type Score = i64; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + #[pallet::storage] + #[pallet::getter(fn approved_citizen_address)] + pub type ApprovedCitizenAddress = StorageValue<_, Vec, ValueQuery>; // Its set, add element through binary_search + + #[pallet::storage] + #[pallet::getter(fn positive_externality_score)] + pub type PositiveExternalityScore = + StorageMap<_, Blake2_128Concat, T::AccountId, Score, ValueQuery>; + + // Keep winning representatives of department in shared storage + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub approved_citizen_address: Vec, + } + + impl Default for GenesisConfig { + fn default() -> Self { + Self { + approved_citizen_address: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + >::put(self.approved_citizen_address.clone()); + } + } + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + CitizenNotApproved, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + /// An example dispatchable that takes a singles value as a parameter, writes the value to + /// storage and emits an event. This function must be dispatched by a signed extrinsic. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::do_something())] + pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + // This function will return an error if the extrinsic is not signed. + // https://docs.substrate.io/main-docs/build/origins/ + let who = ensure_signed(origin)?; + + // Update storage. + >::put(something); + + // Emit an event. + Self::deposit_event(Event::SomethingStored { something, who }); + // Return a successful DispatchResultWithPostInfo + Ok(()) + } + + /// An example dispatchable that may throw a custom error. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::cause_error())] + pub fn cause_error(origin: OriginFor) -> DispatchResult { + let _who = ensure_signed(origin)?; + + // Read a value from storage. + match >::get() { + // Return an error if the value has not been set. + None => return Err(Error::::NoneValue.into()), + Some(old) => { + // Increment the value read from storage; will error in the event of overflow. + let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; + // Update the value in storage with the incremented result. + >::put(new); + Ok(()) + } + } + } + } +} diff --git a/custom-pallets/shared-storage/src/mock.rs b/custom-pallets/shared-storage/src/mock.rs new file mode 100644 index 0000000..67ce67d --- /dev/null +++ b/custom-pallets/shared-storage/src/mock.rs @@ -0,0 +1,61 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + TemplateModule: pallet_template, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} diff --git a/custom-pallets/shared-storage/src/tests.rs b/custom-pallets/shared-storage/src/tests.rs new file mode 100644 index 0000000..0210475 --- /dev/null +++ b/custom-pallets/shared-storage/src/tests.rs @@ -0,0 +1,33 @@ +use crate::{mock::*, Error, Event}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + // Dispatch a signed extrinsic. + assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); + // Read pallet storage and assert an expected result. + assert_eq!(TemplateModule::something(), Some(42)); + // Assert that the correct event was deposited + System::assert_last_event( + Event::SomethingStored { + something: 42, + who: 1, + } + .into(), + ); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the expected error is thrown when no value is present. + assert_noop!( + TemplateModule::cause_error(RuntimeOrigin::signed(1)), + Error::::NoneValue + ); + }); +} diff --git a/pallets/shared-storage/src/types.rs b/custom-pallets/shared-storage/src/types.rs similarity index 100% rename from pallets/shared-storage/src/types.rs rename to custom-pallets/shared-storage/src/types.rs diff --git a/pallets/schelling-game-shared/src/weights.rs b/custom-pallets/shared-storage/src/weights.rs similarity index 100% rename from pallets/schelling-game-shared/src/weights.rs rename to custom-pallets/shared-storage/src/weights.rs diff --git a/custom-pallets/sortition-sum-game/Cargo.toml b/custom-pallets/sortition-sum-game/Cargo.toml new file mode 100644 index 0000000..adeb120 --- /dev/null +++ b/custom-pallets/sortition-sum-game/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "pallet-sortition-sum-game" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +frame-benchmarking = { workspace = true, optional = true } +trait-sortition-sum-game = { workspace = true } + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/shared-storage/README.md b/custom-pallets/sortition-sum-game/README.md similarity index 100% rename from pallets/shared-storage/README.md rename to custom-pallets/sortition-sum-game/README.md diff --git a/custom-pallets/sortition-sum-game/src/benchmarking.rs b/custom-pallets/sortition-sum-game/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/sortition-sum-game/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/sortition-sum-game/src/extras.rs b/custom-pallets/sortition-sum-game/src/extras.rs new file mode 100644 index 0000000..8473ba9 --- /dev/null +++ b/custom-pallets/sortition-sum-game/src/extras.rs @@ -0,0 +1,296 @@ +use crate::*; + +impl SortitionSumGameLink for Pallet { + type SumTreeName = SumTreeNameType; + type AccountId = AccountIdOf; + fn create_tree_link(key: Self::SumTreeName, k: u64) -> DispatchResult { + Self::create_tree(key, k) + } + + fn set_link(key: Self::SumTreeName, value: u64, citizen_id: Self::AccountId) -> DispatchResult { + Self::set(key, value, citizen_id) + } + fn stake_of_link( + key: Self::SumTreeName, + citizen_id: Self::AccountId, + ) -> Result, DispatchError> { + Self::stake_of(key, citizen_id) + } + fn draw_link( + key: Self::SumTreeName, + draw_number: u64, + ) -> Result { + Self::draw(key, draw_number) + } + fn remove_tree_link(key: Self::SumTreeName) -> DispatchResult { + Self::remove_tree(key) + } +} + +impl Pallet { + // SortitionSumTree + pub fn create_tree(key: SumTreeNameType, k: u64) -> DispatchResult { + if k < 1 { + Err(Error::::KMustGreaterThanOne)? + } + let tree_option = >::get(&key); + match tree_option { + Some(_tree) => Err(Error::::TreeAlreadyExists)?, + None => { + let mut sum_tree = SortitionSumTree { + k, + stack: Vec::new(), + nodes: Vec::new(), + ids_to_node_indexes: BTreeMap::new(), + node_indexes_to_ids: BTreeMap::new(), + }; + + sum_tree.nodes.push(0); + + >::insert(&key, &sum_tree); + } + } + Ok(()) + } + + pub fn set(key: SumTreeNameType, value: u64, citizen_id: AccountIdOf) -> DispatchResult { + let tree_option = >::get(&key); + + match tree_option { + None => Err(Error::::TreeDoesnotExist)?, + Some(mut tree) => match tree.ids_to_node_indexes.get(&citizen_id) { + Some(tree_index_data) => { + let tree_index = *tree_index_data; + if tree_index == 0 { + Self::if_tree_index_zero(value, citizen_id, tree, tree_index, key); + } else { + // Existing node + if value == 0 { + let value = tree.nodes[tree_index as usize]; + tree.nodes[tree_index as usize] = 0; + tree.stack.push(tree_index); + tree.ids_to_node_indexes.remove(&citizen_id); + tree.node_indexes_to_ids.remove(&tree_index); + + // UpdateParents 🟥 + Self::update_parents(tree, tree_index, false, value, key); + } else if value != tree.nodes[tree_index as usize] { + let plus_or_minus = tree.nodes[tree_index as usize] <= value; + let plus_or_minus_value = if plus_or_minus { + value + .checked_sub(tree.nodes[tree_index as usize]) + .ok_or("StorageOverflow")? + } else { + (tree.nodes[tree_index as usize]) + .checked_sub(value) + .ok_or("StorageOverflow")? + }; + tree.nodes[tree_index as usize] = value; + + // update parents 🟥 + Self::update_parents( + tree, + tree_index, + plus_or_minus, + plus_or_minus_value, + key, + ); + } + } + } + + None => { + Self::if_tree_index_zero(value, citizen_id, tree, 0, key); + } + }, + } + + Ok(()) + } + + fn update_parents( + mut tree: SortitionSumTree>, + tree_index: u64, + plus_or_minus: bool, + value: u64, + key: SumTreeNameType, + ) { + let mut parent_index = tree_index; + while parent_index != 0 { + parent_index = (parent_index - 1) / tree.k; + tree.nodes[parent_index as usize] = if plus_or_minus { + (tree.nodes[parent_index as usize]) + .checked_add(value) + .expect("StorageOverflow") + } else { + (tree.nodes[parent_index as usize]) + .checked_sub(value) + .expect("StorageOverflow") + }; + + >::insert(&key, &tree); + } + } + fn if_tree_index_zero( + value: u64, + citizen_id: AccountIdOf, + mut tree: SortitionSumTree>, + mut tree_index: u64, + key: SumTreeNameType, + ) { + // No existing node. + if value != 0 { + // Non zero value. + // Append. + // Add node. + if tree.stack.len() == 0 { + // No vacant spots. + // Get the index and append the value. + tree_index = tree.nodes.len() as u64; + tree.nodes.push(value); + + // println!("{}", tree_index); + + // Potentially append a new node and make the parent a sum node. + if tree_index != 1 && (tree_index - 1) % tree.k == 0 { + // Is first child. + let parent_index = tree_index / tree.k; + let parent_id = tree.node_indexes_to_ids.get(&parent_index).unwrap().clone(); + let new_index = tree_index + 1; + tree.nodes + .push(*tree.nodes.get(parent_index as usize).unwrap()); + tree.node_indexes_to_ids.remove(&parent_index); + tree.ids_to_node_indexes + .insert(parent_id.clone(), new_index); + tree.node_indexes_to_ids.insert(new_index, parent_id); + } + } else { + let tree_index = tree.stack.get(tree.stack.len() - 1); + tree.nodes[*tree_index.unwrap() as usize] = value; + tree.stack.pop(); + } + + tree.ids_to_node_indexes + .insert(citizen_id.clone(), tree_index); + tree.node_indexes_to_ids.insert(tree_index, citizen_id); + + // update_parents 🟥 + + Self::update_parents(tree, tree_index, true, value, key); + } + } + + pub fn stake_of( + key: SumTreeNameType, + citizen_id: AccountIdOf, + ) -> Result, DispatchError> { + let tree_option = >::get(&key); + match tree_option { + None => Err(Error::::TreeDoesnotExist)?, + Some(tree) => { + let tree_index_data; + match tree.ids_to_node_indexes.get(&citizen_id) { + Some(v) => tree_index_data = v, + None => return Ok(None), + } + + let value: u64; + let tree_index = *tree_index_data; + if tree_index == 0 { + value = 0; + } else { + value = tree.nodes[tree_index as usize]; + } + Ok(Some(value)) + } + } + } + + pub fn draw( + key: SumTreeNameType, + draw_number: u64, + ) -> Result, DispatchError> { + let tree_option = >::get(&key); + + match tree_option { + None => Err(Error::::TreeDoesnotExist)?, + Some(tree) => { + let mut tree_index = 0; + let mut current_draw_number = draw_number % tree.nodes[0]; + + while (tree.k * tree_index) + 1 < (tree.nodes.len() as u64) { + for i in 1..tree.k + 1 { + let node_index = (tree.k * tree_index) + i; + let node_value = tree.nodes[node_index as usize]; + + if current_draw_number >= node_value { + current_draw_number -= node_value; + } else { + tree_index = node_index; + break; + } + } + } + let account_id = tree.node_indexes_to_ids.get(&tree_index).unwrap().clone(); + Ok(account_id) + } + } + } + + /** + * @dev Query the leaves of a tree. Note that if `startIndex == 0`, the tree is empty and the root node will be returned. + * @param key The key of the tree to get the leaves from. + * @param cursor The pagination cursor. + * @param count The number of items to return. + * @return The index at which leaves start, the values of the returned leaves, and whether there are more for pagination. + * `O(n)` where + * `n` is the maximum number of nodes ever appended. + */ + pub fn query_leafs( + key: SumTreeNameType, + cursor: u64, + count: u64, + ) -> Result<(u64, Vec, bool), DispatchError> { + let tree_option = >::get(&key); + + match tree_option { + None => Err(Error::::TreeDoesnotExist)?, + Some(tree) => { + let mut start_index = 0; + for i in 0..tree.nodes.len() { + if (tree.k * i as u64) + 1 >= tree.nodes.len() as u64 { + start_index = i as u64; + break; + } + } + let loop_start_index = start_index + cursor; + + // let value = if loop_start_index + count > tree.nodes.len() as u64 { + // tree.nodes.len() as u64 - loop_start_index + // } else { + // count + // }; + + let mut values = Vec::new(); + let mut values_index = 0; + let mut has_more = false; + for j in loop_start_index..tree.nodes.len() as u64 { + if values_index < count { + values.push(tree.nodes[j as usize]); + values_index = values_index + 1; + } else { + has_more = true; + break; + } + } + + Ok((start_index, values, has_more)) + } + } + } + + pub fn remove_tree(key: SumTreeNameType) -> DispatchResult { + >::remove(&key); + Ok(()) + } +} diff --git a/custom-pallets/sortition-sum-game/src/lib.rs b/custom-pallets/sortition-sum-game/src/lib.rs new file mode 100644 index 0000000..25f37aa --- /dev/null +++ b/custom-pallets/sortition-sum-game/src/lib.rs @@ -0,0 +1,108 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +mod extras; +pub mod types; + +use crate::types::{SortitionSumTree, SumTreeName}; +use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; +use frame_system::pallet_prelude::*; +use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; +use trait_sortition_sum_game::SortitionSumGameLink; +type AccountIdOf = ::AccountId; +pub type BlockNumberOf = BlockNumberFor; +type SumTreeNameType = SumTreeName, BlockNumberOf>; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + } + + #[pallet::storage] + #[pallet::getter(fn sortition_sum_trees)] + pub type SortitionSumTrees = + StorageMap<_, Blake2_128Concat, SumTreeNameType, SortitionSumTree>>; + + #[pallet::error] + pub enum Error { + NoneValue, + StorageOverflow, + KMustGreaterThanOne, + TreeAlreadyExists, + TreeDoesnotExist, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + /// An example dispatchable that takes a singles value as a parameter, writes the value to + /// storage and emits an event. This function must be dispatched by a signed extrinsic. + + /// An example dispatchable that may throw a custom error. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::cause_error())] + pub fn cause_error(origin: OriginFor) -> DispatchResult { + let _who = ensure_signed(origin)?; + + // Read a value from storage. + match >::get() { + // Return an error if the value has not been set. + None => return Err(Error::::NoneValue.into()), + Some(old) => { + // Increment the value read from storage; will error in the event of overflow. + let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; + // Update the value in storage with the incremented result. + >::put(new); + Ok(()) + } + } + } + } +} diff --git a/custom-pallets/sortition-sum-game/src/mock.rs b/custom-pallets/sortition-sum-game/src/mock.rs new file mode 100644 index 0000000..67ce67d --- /dev/null +++ b/custom-pallets/sortition-sum-game/src/mock.rs @@ -0,0 +1,61 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + TemplateModule: pallet_template, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} diff --git a/custom-pallets/sortition-sum-game/src/tests.rs b/custom-pallets/sortition-sum-game/src/tests.rs new file mode 100644 index 0000000..012e285 --- /dev/null +++ b/custom-pallets/sortition-sum-game/src/tests.rs @@ -0,0 +1,48 @@ +use crate::{mock::*, types::SumTreeName, Error, Event}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + + let key = SumTreeName::ProfileValidation { + citizen_address: 1, + block_number: 10, + }; + assert_ok!(TemplateModule::create_tree(key.clone(), 5)); + assert_ok!(TemplateModule::set(key.clone(), 10, 1)); + assert_ok!(TemplateModule::set(key.clone(), 20, 1)); + assert_ok!(TemplateModule::set(key.clone(), 30, 2)); + assert_ok!(TemplateModule::set(key.clone(), 40, 3)); + assert_ok!(TemplateModule::set(key.clone(), 50, 4)); + assert_eq!(TemplateModule::stake_of(key.clone(), 1), Ok(Some(20))); + assert_eq!(TemplateModule::draw(key.clone(), 90), Ok(4)); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let key = SumTreeName::ProfileValidation { + citizen_address: 1, + block_number: 10, + }; + assert_ok!(TemplateModule::create_tree(key.clone(), 2)); + assert_ok!(TemplateModule::set(key.clone(), 10, 1)); + assert_ok!(TemplateModule::set(key.clone(), 20, 1)); + assert_ok!(TemplateModule::set(key.clone(), 30, 2)); + assert_ok!(TemplateModule::set(key.clone(), 40, 3)); + let data2 = TemplateModule::query_leafs(key.clone(), 0, 5); + println!("{:?}", data2); + assert_ok!(TemplateModule::set(key.clone(), 50, 4)); + assert_ok!(TemplateModule::set(key.clone(), 0, 3)); + + let data2 = TemplateModule::query_leafs(key.clone(), 0, 5); + println!("{:?}", data2); + + let data = TemplateModule::draw(key.clone(), 98); + println!("{:?}", data); + }); +} diff --git a/custom-pallets/sortition-sum-game/src/types.rs b/custom-pallets/sortition-sum-game/src/types.rs new file mode 100644 index 0000000..33ea282 --- /dev/null +++ b/custom-pallets/sortition-sum-game/src/types.rs @@ -0,0 +1,36 @@ +use frame_support::pallet_prelude::*; +use scale_info::TypeInfo; +use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; + +type CitizenId = u64; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, TypeInfo)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum SumTreeName { + ProfileValidation { + citizen_address: AccountId, + block_number: BlockNumber, + }, + PositiveExternality { + user_address: AccountId, + block_number: BlockNumber, + }, + DepartmentRequiredFund { + department_required_fund_id: u64, + block_number: BlockNumber, + }, + ProjectTips { + project_id: u64, + block_number: BlockNumber, + }, +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, TypeInfo)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct SortitionSumTree { + pub k: u64, + pub stack: Vec, + pub nodes: Vec, + pub ids_to_node_indexes: BTreeMap, // citizen id, node index + pub node_indexes_to_ids: BTreeMap, // node index, citizen id +} diff --git a/pallets/shared-storage/src/weights.rs b/custom-pallets/sortition-sum-game/src/weights.rs similarity index 100% rename from pallets/shared-storage/src/weights.rs rename to custom-pallets/sortition-sum-game/src/weights.rs diff --git a/custom-pallets/spaces/Cargo.toml b/custom-pallets/spaces/Cargo.toml new file mode 100644 index 0000000..4b30f6f --- /dev/null +++ b/custom-pallets/spaces/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "pallet-spaces" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false + + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-timestamp = { workspace = true } +sp-std = { workspace = true} + + +pallet-support = { workspace = true } + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/sortition-sum-game/README.md b/custom-pallets/spaces/README.md similarity index 100% rename from pallets/sortition-sum-game/README.md rename to custom-pallets/spaces/README.md diff --git a/custom-pallets/spaces/src/benchmarking.rs b/custom-pallets/spaces/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/spaces/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/spaces/src/extras.rs b/custom-pallets/spaces/src/extras.rs new file mode 100644 index 0000000..616faa1 --- /dev/null +++ b/custom-pallets/spaces/src/extras.rs @@ -0,0 +1,8 @@ +use crate::*; + +impl Pallet { + /// Get `Space` by id from the storage or return `SpaceNotFound` error. + pub fn require_space(space_id: SpaceId) -> Result, DispatchError> { + Ok(Self::space_by_id(space_id).ok_or(Error::::SpaceNotFound)?) + } +} diff --git a/custom-pallets/spaces/src/lib.rs b/custom-pallets/spaces/src/lib.rs new file mode 100644 index 0000000..1d43c30 --- /dev/null +++ b/custom-pallets/spaces/src/lib.rs @@ -0,0 +1,136 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +pub mod extras; +pub mod types; + +use sp_std::prelude::*; +// use scale_info::prelude::format; +use crate::types::RESERVED_SPACE_COUNT; + +use frame_support::pallet_prelude::{DispatchResult, *}; +use frame_system::pallet_prelude::*; +use pallet_support::{Content, SpaceId}; +use types::Space; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config + pallet_timestamp::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + #[pallet::type_value] + pub fn DefaultForNextSpaceId() -> SpaceId { + RESERVED_SPACE_COUNT + 1 + } + + /// The next space id. + #[pallet::storage] + #[pallet::getter(fn next_space_id)] + pub type NextSpaceId = StorageValue<_, SpaceId, ValueQuery, DefaultForNextSpaceId>; + + /// Get the details of a space by its' id. + #[pallet::storage] + #[pallet::getter(fn space_by_id)] + pub type SpaceById = StorageMap<_, Twox64Concat, SpaceId, Space>; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + + SpaceNotFound, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + /// An example dispatchable that takes a singles value as a parameter, writes the value to + /// storage and emits an event. This function must be dispatched by a signed extrinsic. + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::do_something())] + pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + // This function will return an error if the extrinsic is not signed. + // https://docs.substrate.io/main-docs/build/origins/ + let who = ensure_signed(origin)?; + + // Update storage. + >::put(something); + + // Emit an event. + Self::deposit_event(Event::SomethingStored { something, who }); + // Return a successful DispatchResultWithPostInfo + Ok(()) + } + + /// An example dispatchable that may throw a custom error. + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::cause_error())] + pub fn cause_error(origin: OriginFor) -> DispatchResult { + let _who = ensure_signed(origin)?; + + // Read a value from storage. + match >::get() { + // Return an error if the value has not been set. + None => return Err(Error::::NoneValue.into()), + Some(old) => { + // Increment the value read from storage; will error in the event of overflow. + let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; + // Update the value in storage with the incremented result. + >::put(new); + Ok(()) + } + } + } + } +} diff --git a/custom-pallets/spaces/src/mock.rs b/custom-pallets/spaces/src/mock.rs new file mode 100644 index 0000000..aa11bba --- /dev/null +++ b/custom-pallets/spaces/src/mock.rs @@ -0,0 +1,73 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, parameter_types, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + TemplateModule: pallet_template, + Timestamp: pallet_timestamp, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} diff --git a/custom-pallets/spaces/src/tests.rs b/custom-pallets/spaces/src/tests.rs new file mode 100644 index 0000000..0210475 --- /dev/null +++ b/custom-pallets/spaces/src/tests.rs @@ -0,0 +1,33 @@ +use crate::{mock::*, Error, Event}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + // Dispatch a signed extrinsic. + assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); + // Read pallet storage and assert an expected result. + assert_eq!(TemplateModule::something(), Some(42)); + // Assert that the correct event was deposited + System::assert_last_event( + Event::SomethingStored { + something: 42, + who: 1, + } + .into(), + ); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the expected error is thrown when no value is present. + assert_noop!( + TemplateModule::cause_error(RuntimeOrigin::signed(1)), + Error::::NoneValue + ); + }); +} diff --git a/custom-pallets/spaces/src/types.rs b/custom-pallets/spaces/src/types.rs new file mode 100644 index 0000000..45bda76 --- /dev/null +++ b/custom-pallets/spaces/src/types.rs @@ -0,0 +1,54 @@ +use frame_support::pallet_prelude::*; +use scale_info::TypeInfo; + +use super::*; + +use pallet_support::{new_who_and_when, WhoAndWhenOf}; + +pub const FIRST_SPACE_ID: u64 = 1; +pub const RESERVED_SPACE_COUNT: u64 = 1000; + +/// Information about a space's owner, its' content, visibility and custom permissions. +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct Space { + /// Unique sequential identifier of a space. Examples of space ids: `1`, `2`, `3`, and so on. + pub id: SpaceId, + + pub created: WhoAndWhenOf, + /// True, if the content of this space was edited. + pub edited: bool, + + /// The current owner of a given space. + pub owner: T::AccountId, + + // The next fields can be updated by the owner: + pub content: Content, + + /// Hidden field is used to recommend to end clients (web and mobile apps) that a particular + /// space and its' posts should not be shown. + pub hidden: bool, +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, RuntimeDebug, TypeInfo)] +pub struct SpaceUpdate { + pub content: Option, + pub hidden: Option, +} + +impl Space { + pub fn new(id: SpaceId, created_by: T::AccountId, content: Content) -> Self { + Space { + id, + created: new_who_and_when::(created_by.clone()), + edited: false, + owner: created_by, + content, + hidden: false, + } + } + + pub fn is_owner(&self, account: &T::AccountId) -> bool { + self.owner == *account + } +} diff --git a/pallets/sortition-sum-game/src/weights.rs b/custom-pallets/spaces/src/weights.rs similarity index 100% rename from pallets/sortition-sum-game/src/weights.rs rename to custom-pallets/spaces/src/weights.rs diff --git a/custom-pallets/support/Cargo.toml b/custom-pallets/support/Cargo.toml new file mode 100644 index 0000000..8bcb709 --- /dev/null +++ b/custom-pallets/support/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "pallet-support" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +pallet-balances = { workspace = true } +sp-io = { workspace = true } +log = {workspace = true} +sp-runtime = { workspace = true } +sp-std = { workspace = true} +sp-arithmetic = { workspace = true } +pallet-timestamp = { workspace = true } +strum = { workspace = true } + + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "scale-info/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "pallet-balances/std", + "log/std", + "sp-runtime/std", + "sp-std/std", + "sp-arithmetic/std", + "strum/std", +] + +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/custom-pallets/support/src/lib.rs b/custom-pallets/support/src/lib.rs new file mode 100644 index 0000000..1379e2b --- /dev/null +++ b/custom-pallets/support/src/lib.rs @@ -0,0 +1,152 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; +// use frame_support::sp_std::{vec::Vec}; +use sp_std::{vec, vec::Vec}; + +pub type SpaceId = u64; +pub type PostId = u64; + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct WhoAndWhen { + pub account: AccountId, + pub block: BlockNumber, + pub time: Moment, +} + +pub type WhoAndWhenOf = WhoAndWhen< + ::AccountId, + BlockNumberFor, + ::Moment, +>; + +pub fn new_who_and_when( + account: T::AccountId, +) -> WhoAndWhen, T::Moment> +where + T: frame_system::Config + pallet_timestamp::Config, +{ + WhoAndWhen { + account, + block: frame_system::Pallet::::block_number(), + time: pallet_timestamp::Pallet::::now(), + } +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct WhenDetails { + pub block: BlockNumber, + pub time: Moment, +} + +pub type WhenDetailsOf = WhenDetails, ::Moment>; + +pub fn new_when_details() -> WhenDetails, T::Moment> +where + T: frame_system::Config + pallet_timestamp::Config, +{ + WhenDetails { + block: frame_system::Pallet::::block_number(), + time: pallet_timestamp::Pallet::::now(), + } +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub enum Content { + /// No content. + None, + /// A raw vector of bytes. + Other(Vec), + /// IPFS CID v0 of content. + IPFS(Vec), +} + +impl From for Vec { + fn from(content: Content) -> Vec { + match content { + Content::None => vec![], + Content::Other(vec_u8) => vec_u8, + Content::IPFS(vec_u8) => vec_u8, + } + } +} + +impl Default for Content { + fn default() -> Self { + Self::None + } +} + +impl Content { + pub fn is_none(&self) -> bool { + self == &Self::None + } + + pub fn is_some(&self) -> bool { + !self.is_none() + } + + pub fn is_ipfs(&self) -> bool { + matches!(self, Self::IPFS(_)) + } +} + +#[derive(Encode, Decode, RuntimeDebug, strum::IntoStaticStr)] +pub enum ContentError { + /// IPFS CID is invalid. + InvalidIpfsCid, + /// `Other` content type is not yet supported. + OtherContentTypeNotSupported, + /// Content type is `None`. + ContentIsEmpty, +} + +impl From for DispatchError { + fn from(err: ContentError) -> DispatchError { + Self::Other(err.into()) + } +} + +pub fn ensure_content_is_valid(content: Content) -> DispatchResult { + match content { + Content::None => Ok(()), + Content::Other(_) => Err(ContentError::OtherContentTypeNotSupported.into()), + Content::IPFS(ipfs_cid) => { + let len = ipfs_cid.len(); + // IPFS CID v0 is 46 bytes. + // IPFS CID v1 is 59 bytes. + ensure!(len == 46 || len == 59, ContentError::InvalidIpfsCid); + Ok(()) + } + } +} + +/// Ensure that a given content is not `None`. +pub fn ensure_content_is_some(content: &Content) -> DispatchResult { + ensure!(content.is_some(), ContentError::ContentIsEmpty); + Ok(()) +} + +pub fn remove_from_vec(vector: &mut Vec, element: F) { + if let Some(index) = vector.iter().position(|x| *x == element) { + vector.swap_remove(index); + } +} + +pub fn remove_from_bounded_vec(vector: &mut BoundedVec, element: F) { + if let Some(index) = vector.iter().position(|x| *x == element) { + vector.swap_remove(index); + } +} + +pub fn bool_to_option(value: bool) -> Option { + if value { + Some(value) + } else { + None + } +} diff --git a/custom-pallets/template/Cargo.toml b/custom-pallets/template/Cargo.toml new file mode 100644 index 0000000..05ec4d3 --- /dev/null +++ b/custom-pallets/template/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "pallet-template" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/spaces/README.md b/custom-pallets/template/README.md similarity index 100% rename from pallets/spaces/README.md rename to custom-pallets/template/README.md diff --git a/custom-pallets/template/src/benchmarking.rs b/custom-pallets/template/src/benchmarking.rs new file mode 100644 index 0000000..26fff6e --- /dev/null +++ b/custom-pallets/template/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/template/src/lib.rs b/custom-pallets/template/src/lib.rs new file mode 100644 index 0000000..18f5cb5 --- /dev/null +++ b/custom-pallets/template/src/lib.rs @@ -0,0 +1,108 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + } + + // The pallet's runtime storage items. + // https://docs.substrate.io/main-docs/build/runtime-storage/ + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/main-docs/build/events-errors/ + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored { something: u32, who: T::AccountId }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + /// An example dispatchable that takes a singles value as a parameter, writes the value to + /// storage and emits an event. This function must be dispatched by a signed extrinsic. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::do_something())] + pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + // This function will return an error if the extrinsic is not signed. + // https://docs.substrate.io/main-docs/build/origins/ + let who = ensure_signed(origin)?; + + // Update storage. + >::put(something); + + // Emit an event. + Self::deposit_event(Event::SomethingStored { something, who }); + // Return a successful DispatchResultWithPostInfo + Ok(()) + } + + /// An example dispatchable that may throw a custom error. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::cause_error())] + pub fn cause_error(origin: OriginFor) -> DispatchResult { + let _who = ensure_signed(origin)?; + + // Read a value from storage. + match >::get() { + // Return an error if the value has not been set. + None => return Err(Error::::NoneValue.into()), + Some(old) => { + // Increment the value read from storage; will error in the event of overflow. + let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; + // Update the value in storage with the incremented result. + >::put(new); + Ok(()) + } + } + } + } +} diff --git a/custom-pallets/template/src/mock.rs b/custom-pallets/template/src/mock.rs new file mode 100644 index 0000000..67ce67d --- /dev/null +++ b/custom-pallets/template/src/mock.rs @@ -0,0 +1,61 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + TemplateModule: pallet_template, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} diff --git a/custom-pallets/template/src/tests.rs b/custom-pallets/template/src/tests.rs new file mode 100644 index 0000000..0210475 --- /dev/null +++ b/custom-pallets/template/src/tests.rs @@ -0,0 +1,33 @@ +use crate::{mock::*, Error, Event}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + // Dispatch a signed extrinsic. + assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); + // Read pallet storage and assert an expected result. + assert_eq!(TemplateModule::something(), Some(42)); + // Assert that the correct event was deposited + System::assert_last_event( + Event::SomethingStored { + something: 42, + who: 1, + } + .into(), + ); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the expected error is thrown when no value is present. + assert_noop!( + TemplateModule::cause_error(RuntimeOrigin::signed(1)), + Error::::NoneValue + ); + }); +} diff --git a/pallets/spaces/src/weights.rs b/custom-pallets/template/src/weights.rs similarity index 100% rename from pallets/spaces/src/weights.rs rename to custom-pallets/template/src/weights.rs diff --git a/docker/container-chain-evm-template.Dockerfile b/docker/container-chain-evm-template.Dockerfile new file mode 100644 index 0000000..8107000 --- /dev/null +++ b/docker/container-chain-evm-template.Dockerfile @@ -0,0 +1,36 @@ +# Node for Container-chain-evm-template +# +# Requires to run from repository root and to copy the binary in the build folder (part of the release workflow) + +FROM docker.io/library/ubuntu:20.04 AS builder + +RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates + +FROM debian:bookworm-slim +LABEL maintainer "gorka@moondancelabs.com" +LABEL description="Binary for container-chain-template-evm Collator" + +RUN useradd -m -u 1000 -U -s /bin/sh -d /container-chain-template-evm container-chain-template-evm && \ + mkdir -p /container-chain-template-evm/.local/share && \ + mkdir /data && \ + chown -R container-chain-template-evm:container-chain-template-evm /data && \ + ln -s /data /container-chain-template-evm/.local/share/container-chain-template-evm && \ + rm -rf /usr/sbin + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +USER container-chain-template-evm + +COPY --chown=container-chain-template-evm build/container-chain-frontier-node* /container-chain-template-evm +RUN chmod uog+x /container-chain-template-evm/container-chain-frontier* + +# 30333 for parachain p2p +# 30334 for relaychain p2p +# 9933 for RPC call +# 9944 for Websocket +# 9615 for Prometheus (metrics) +EXPOSE 30333 30334 9933 9944 9615 + +VOLUME ["/data"] + +ENTRYPOINT ["/container-chain-template-evm/container-chain-frontier-node"] \ No newline at end of file diff --git a/docker/container-chain-simple-template.Dockerfile b/docker/container-chain-simple-template.Dockerfile new file mode 100644 index 0000000..64f55da --- /dev/null +++ b/docker/container-chain-simple-template.Dockerfile @@ -0,0 +1,36 @@ +# Node for Container-chain-simple-template +# +# Requires to run from repository root and to copy the binary in the build folder (part of the release workflow) + +FROM docker.io/library/ubuntu:20.04 AS builder + +RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates + +FROM debian:bookworm-slim +LABEL maintainer "gorka@moondancelabs.com" +LABEL description="Binary for simple container chain template node" + +RUN useradd -m -u 1000 -U -s /bin/sh -d /container-chain-template-simple container-chain-template-simple && \ + mkdir -p /container-chain-template-simple/.local/share && \ + mkdir /data && \ + chown -R container-chain-template-simple:container-chain-template-simple /data && \ + ln -s /data /container-chain-template-simple/.local/share/container-chain-template-simple && \ + rm -rf /usr/sbin + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +USER container-chain-template-simple + +COPY --chown=container-chain-template-simple build/container-chain-simple-node* /container-chain-template-simple +RUN chmod uog+x /container-chain-template-simple/container-chain-simple* + +# 30333 for parachain p2p +# 30334 for relaychain p2p +# 9933 for RPC call +# 9944 for Websocket +# 9615 for Prometheus (metrics) +EXPOSE 30333 30334 9933 9944 9615 + +VOLUME ["/data"] + +ENTRYPOINT ["/container-chain-template-simple/container-chain-simple-node"] \ No newline at end of file diff --git a/docker/tanssi-srtool.Dockerfile b/docker/tanssi-srtool.Dockerfile new file mode 100644 index 0000000..ef607e2 --- /dev/null +++ b/docker/tanssi-srtool.Dockerfile @@ -0,0 +1,11 @@ +ARG SRTOOL_IMAGE_TAG +ARG SRTOOL_IMAGE_REPO + +FROM ${SRTOOL_IMAGE_REPO}:${SRTOOL_IMAGE_TAG} + +USER root + +RUN apt-get update && \ + apt-get install openssh-server -y + +USER 1001 \ No newline at end of file diff --git a/docker/tanssi.Dockerfile b/docker/tanssi.Dockerfile new file mode 100644 index 0000000..cbdba58 --- /dev/null +++ b/docker/tanssi.Dockerfile @@ -0,0 +1,40 @@ +# Node for Tanssi +# +# Requires to run from repository root and to copy the binary in the build folder (part of the release workflow) + +FROM docker.io/library/ubuntu:20.04 AS builder + +RUN apt-get update && apt-get install -y ca-certificates lsof && update-ca-certificates + +FROM debian:bookworm-slim +LABEL maintainer "gorka@moondancelabs.com" +LABEL description="Binary for Tanssi Collator" + +RUN useradd -m -u 1000 -U -s /bin/sh -d /tanssi tanssi && \ + mkdir -p /tanssi/.local/share && \ + mkdir /data && \ + chown -R tanssi:tanssi /data && \ + ln -s /data /tanssi/.local/share/tanssi && \ + rm -rf /usr/sbin + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +USER tanssi + +COPY --chown=tanssi build/tanssi-node* /tanssi +RUN chmod uog+x /tanssi/tanssi* + +# 30333 for parachain p2p +# 30334 for relaychain p2p +# 30335 for container p2p +# 9933 for RPC call +# 9944 for Websocket +# 9615 for Prometheus (metrics) +# 9935 for RPC call container (if we want to expose this) +# 9946 for Websocket container (if we want to expose this) +# 9617 for Prometheus container (metrics) +EXPOSE 30333 30334 30335 9933 9944 9615 9935 9946 9617 + +VOLUME ["/data"] + +ENTRYPOINT ["/tanssi/tanssi-node"] diff --git a/docs/benchmarking.md b/docs/benchmarking.md new file mode 100644 index 0000000..a6eb026 --- /dev/null +++ b/docs/benchmarking.md @@ -0,0 +1,147 @@ +# Benchmarking +This guide explains how to use the benchmarking tool under `tools/benchmarking.sh` for better developer experience + +## Benchmarking pallets vs benchmarking runtimes +Let's first explain the difference between benchmarking a pallet and benchmarking a runtime. When we benchmark a pallet, a public `trait WeightInfo` is created. The pallet is going to ask for an implementation of this trait in the associated `Config` type. Obviously when we benchmark a pallet this trait is by default implemented for the empty tuple and generic `substrateWeight` struct. Here is an example: + +``` +/// Weight functions needed for pallet_data_preservers. +pub trait WeightInfo { + fn set_boot_nodes(x: u32, y: u32, ) -> Weight; +} + +/// Weights for pallet_data_preservers using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:0 w:1) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + /// The range of component `y` is `[1, 10]`. + fn set_boot_nodes(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 10_703_000 picoseconds. + Weight::from_parts(9_788_229, 3660) + // Standard Error: 170 + .saturating_add(Weight::from_parts(7_964, 0).saturating_mul(x.into())) + // Standard Error: 3_552 + .saturating_add(Weight::from_parts(334_296, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:0 w:1) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + /// The range of component `y` is `[1, 10]`. + fn set_boot_nodes(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 10_703_000 picoseconds. + Weight::from_parts(9_788_229, 3660) + // Standard Error: 170 + .saturating_add(Weight::from_parts(7_964, 0).saturating_mul(x.into())) + // Standard Error: 3_552 + .saturating_add(Weight::from_parts(334_296, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} +``` + +When we benchmark a runtime, we generate structs that implement the `WeightInfo` trait from all the pallets. This means that we don't create a new trait specific for a runtime: + +``` +/// Weights for pallet_data_preservers using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_data_preservers::WeightInfo for SubstrateWeight { + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:0 w:1) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + /// The range of component `y` is `[1, 10]`. + fn set_boot_nodes(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 15_901_000 picoseconds. + Weight::from_parts(13_983_853, 3660) + // Standard Error: 154 + .saturating_add(Weight::from_parts(12_442, 0).saturating_mul(x.into())) + // Standard Error: 3_215 + .saturating_add(Weight::from_parts(452_262, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} +``` +The two files generated are quite different, hence the reason for us having two different templates to do benchmarking, which can be found at `benchmarking/frame-weight-runtime-template.hbs` and `benchmarking/frame-weight-template.hbs`. + +## Using the benchmarking tool + +The first thing we need to do is compile all runtime with `runtime-benchmarks` and `fast-runtime` features: + +``` +cargo build --features=fast-runtime,runtime-benchmarks --release +``` + +This will get the binaries ready for benchmarking purposes. + +The next step is to to use the `tools/benchmarking.sh` script. There are four environmental variables you can set before using this tool: + +- `BINARY`: The binary you want to use for benchmarking. If not specified, by default uses `target/release/tanssi-node` +- `CHAIN`: The chain that you want to use. By default it uses `dev` +- `OUTPUT_PATH`: The output path for the generated benchmarks. By default it uses `tmp` +- `TEMPLATE_PATH`: The template to use to generate the benchmarking file. By default it uses the pallet one, i.e., `benchmarking/frame-weight-pallet-template`. + +Additional, the script is going to ask for two arguments: +- the pallet that you want to benchmark. If you want to benchmark all pallets, you need to pass `"*"`. Otherwise, you pase under quotes the pallet to be benchmarked, e.g., `"pallet_pooled_staking"`. +- the extrinsic that you want to benchmark. If you want to benchmark all extrinsics, you pass `"*"`. Otherwise you pass under quotes the extrinsic to be benchmarked, e.g., `"request_delegate"` + +## Useful examples + +### Benchmarking all pallets for the dancebox runtime + +``` +TEMPLATE_PATH=benchmarking/frame-weight-runtime-template.hbs OUTPUT_PATH=runtime/dancebox/src/weights ./tools/benchmarking.sh "*" "*" +``` + +### Benchmarking all pallets for the flashbox runtime + +``` +TEMPLATE_PATH=benchmarking/frame-weight-runtime-template.hbs OUTPUT_PATH=runtime/flashbox/src/weights CHAIN=flashbox_dev ./tools/benchmarking.sh "*" "*" +``` + +### Benchmarking all pallets for the container-chain-frontier-runtime + +``` +BINARY=target/release/container-chain-frontier-node TEMPLATE_PATH=benchmarking/frame-weight-runtime-template.hbs OUTPUT_PATH=container-chains/templates/frontier/runtime/src/weights ./tools/benchmarking.sh "*" "*" +``` + +### Benchmarking all pallets for the container-chain-simple-runtime + +``` +BINARY=target/release/container-chain-simple-node TEMPLATE_PATH=benchmarking/frame-weight-runtime-template.hbs OUTPUT_PATH=container-chains/templates/simple/runtime/src/weights ./tools/benchmarking.sh "*" "*" +``` + +### Generating weight info trait bound for pallet-pooled-staking + +``` +TEMPLATE_PATH=benchmarking/frame-weight-pallet-template.hbs OUTPUT_PATH=pallets/pooled-staking/src/weights.rs ./tools/benchmarking.sh "pallet_pooled_staking" "*" +``` + +### Generating weight info trait bound for pallet-cc-authorities-noting + +``` +BINARY=target/release/container-chain-simple-node TEMPLATE_PATH=benchmarking/frame-weight-pallet-template.hbs OUTPUT_PATH=../dancekit/container-chain-pallets/authorities-noting/src/weights.rs ./tools/benchmarking.sh "pallet_cc_authorities_noting" "*" +``` diff --git a/docs/clippy.md b/docs/clippy.md new file mode 100644 index 0000000..11154db --- /dev/null +++ b/docs/clippy.md @@ -0,0 +1,28 @@ +Some useful clippy lints, can be added to root `Cargo.toml` for better developer experience + +```toml +# Some lints that can be useful but should not be enabled in CI +# because of false positives and noise in tests +as_conversions = { level = "warn", priority = 1 } +cast_possible_truncation = { level = "warn", priority = 1 } +cognitive_complexity = { level = "warn", priority = 1 } +derive_partial_eq_without_eq = { level = "warn", priority = 1 } +else_if_without_else = { level = "warn", priority = 1 } +future_not_send = { level = "warn", priority = 1 } +redundant_clone = { level = "warn", priority = 1 } +unused_async = { level = "warn", priority = 1 } + +# Restrictions, useful to find places where code can panic +arithmetic_side_effects = { level = "warn", priority = 1 } +expect_used = { level = "warn", priority = 1 } +float_arithmetic = { level = "warn", priority = 1 } +indexing_slicing = { level = "warn", priority = 1 } +missing_panics_doc = { level = "warn", priority = 1 } +todo = { level = "warn", priority = 1 } +unwrap_used = { level = "warn", priority = 1 } + +# Find leftovers from debugging +dbg_macro = { level = "warn", priority = 1 } +print_stderr = { level = "warn", priority = 1 } +print_stdout = { level = "warn", priority = 1 } +``` diff --git a/docs/keep_db_flowchart.dot b/docs/keep_db_flowchart.dot new file mode 100644 index 0000000..76c8575 --- /dev/null +++ b/docs/keep_db_flowchart.dot @@ -0,0 +1,52 @@ +digraph G { + dpi=300; + rankdir=TB; + + node [style=filled, fillcolor="#add8e6"]; + + A [label="Is a collator?", shape=diamond]; + B1 [label="Start partial node to check contents of db", shape=box]; + B2 [label="Has --keep-db flag?", shape=diamond]; + C [label="Check difference between highest block\nin the db and the block according the\norchestrator author-noting pallet. Is it\ngreater than 100 blocks?", shape=diamond]; + D [label="Compare the genesis hash from\norchestrator registrar pallet and the\ngenesis hash according to the local db.\nDoes it match?", shape=diamond]; + E [label="Stop partial node and wait 10 seconds for all the services to stop", shape=box]; + F [label="Full nodes never delete the db", shape=box, fillcolor="#98FB98"]; + G [label="We want to use warp sync instead of downloading\nmore than 100 blocks, and warp sync only works\nif the db is empty, so delete db", shape=box, fillcolor="#FFB6C1"]; + H [label="A genesis mismatch means that a\ncontainer chain which we have in the db\nwas deregistered, and a different\ncontainer chain was registered under the\nsame para id. In that case the node will\nnever be able to sync using the existing\ndb, so we delete it", shape=box, fillcolor="#FFB6C1"]; + I [label="Keep db", shape=box, fillcolor="#98FB98"]; + J [label="Start container chain node", shape=box]; + J2 [label="Stop container chain node", shape=box]; + K [label="Node crashed (panic)", shape=box]; + L [label="Node manually stopped\n(ctrl-c or kill without -9)", shape=box]; + L2 [label="Node killed using kill -9", shape=box]; + M [label="Collator unassigned from container chain", shape=box]; + N [label="Has --keep-db flag or is a full node?\n(only collators delete the db)", shape=diamond]; + O [label="Keep db", shape=box, fillcolor="#98FB98"]; + P [label="Delete db", shape=box, fillcolor="#FFB6C1"]; + + A -> B1 [label=Yes]; + A -> F [label=No]; + B1 -> B2; + B2 -> D [label=Yes]; + B2 -> C [label=No]; + C -> G [label=Yes]; + C -> D [label=No]; + D -> H [label=No]; + D -> I [label=Yes]; + I -> E; + G -> E; + H -> E; + E -> J; + F -> J; + J -> J2; + J2 -> K; + J2 -> L; + J2 -> L2; + J2 -> M; + M -> N; + N -> O [label=Yes]; + N -> P [label=No]; + K -> O; + L -> O; + L2 -> O; +} diff --git a/docs/keep_db_flowchart.png b/docs/keep_db_flowchart.png new file mode 100644 index 0000000..56f0efc Binary files /dev/null and b/docs/keep_db_flowchart.png differ diff --git a/docs/new_code.md b/docs/new_code.md new file mode 100644 index 0000000..3167ef6 --- /dev/null +++ b/docs/new_code.md @@ -0,0 +1,240 @@ +# New codes + + +## Main Cargo.toml + +```toml + +[workspace] +members = [ + "custom-pallets/*", +] + +``` + +```toml +[dependencies] +## New start + +## New pallets +pallet-template = {path = "custom-pallets/template", default-features= false} +pallet-support = {path = "custom-pallets/support", default-features= false} +pallet-spaces = {path= "custom-pallets/spaces", default-features=false } +pallet-sortition-sum-game = {path = "custom-pallets/sortition-sum-game", default-features=false} +pallet-shared-storage = {path = "custom-pallets/shared-storage", default-features = false} +pallet-schelling-game-shared = {path = "custom-pallets/schelling-game-shared", default-features = false } +pallet-profile-validation = {path = "custom-pallets/profile-validation", default-features = false} +pallet-project-tips = {path = "custom-pallets/project-tips", default-features = false} +pallet-positive-externality = { path = "custom-pallets/positive-externality", default-features = false } +pallet-department-funding = { path = "custom-pallets/department-funding", default-features = false } + +## Traits +trait-sortition-sum-game = {path = "traits/trait-sortition-sum-game", default-features=false} +trait-shared-storage = {path= "traits/trait-shared-storage", default-features=false} +trait-schelling-game-shared = {path = "traits/trait-schelling-game-shared", default-features=false} + +## Api +profile-validation-runtime-api = { path = "custom-pallets/profile-validation/profile-validation-runtime-api", default-features = false} +project-tips-runtime-api = { path = "custom-pallets/project-tips/project-tips-runtime-api", default-features = false } +positive-externality-runtime-api = { path = "custom-pallets/positive-externality/positive-externality-runtime-api", default-features = false} +department-funding-runtime-api = { path = "custom-pallets/department-funding/department-funding-runtime-api", default-features = false} + + +## Rpc +profile-validation-rpc = { path = "custom-pallets/profile-validation/profile-validation-rpc", default-features = false} +project-tips-rpc = { path = "custom-pallets/project-tips/project-tips-rpc", default-features = false } +positive-externality-rpc = { path = "custom-pallets/positive-externality/positive-externality-rpc", default-features = false} +department-funding-rpc = { path = "custom-pallets/department-funding/department-funding-rpc", default-features = false} + + + + +## Additional dependancies +sp-arithmetic = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false } +strum = { version = "0.26.2", default-features = false, features = ["derive"] } +num-integer = {default-features = false, version= "0.1.44"} +frame-support-test ={ git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false} +pallet-insecure-randomness-collective-flip = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-v1.6.0", default-features = false} + +## New end + + +``` + + +## Node runtime + +### **node-runtime/Cargo.toml** + +```toml +[dependencies] +pallet-insecure-randomness-collective-flip = { workspace = true } + + +# Local Dependencies +pallet-template = { workspace = true } + +pallet-sortition-sum-game = { workspace = true } +# pallet-election={ workspace = true } +# # election-runtime-api={ workspace = true } +# pallet-posts = { workspace = true } +# pallet-spaces = { workspace = true } +pallet-schelling-game-shared = { workspace = true } +pallet-profile-validation = { workspace = true } +# profile-validation-runtime-api = { workspace = true } +pallet-shared-storage = { workspace = true } +pallet-positive-externality = { workspace = true } +pallet-department-funding = { workspace = true } +pallet-project-tips = { workspace = true } + +profile-validation-runtime-api = { workspace = true } +positive-externality-runtime-api = { workspace = true } +department-funding-runtime-api = { workspace = true } +project-tips-runtime-api = { workspace = true } + + +[features] +default = [ "std" ] +std = [ + "pallet-insecure-randomness-collective-flip/std", + "pallet-template/std", + "pallet-sortition-sum-game/std", + "pallet-schelling-game-shared/std", + "pallet-profile-validation/std", + "pallet-shared-storage/std", + "pallet-positive-externality/std", + "pallet-department-funding/std", + "pallet-project-tips/std", + "profile-validation-runtime-api/std", + "positive-externality-runtime-api/std", + "department-funding-runtime-api/std", + "project-tips-runtime-api/std", +] + +runtime-benchmarks = [ + "pallet-template/runtime-benchmarks", + "pallet-sortition-sum-game/runtime-benchmarks", + "pallet-schelling-game-shared/runtime-benchmarks", + "pallet-profile-validation/runtime-benchmarks", + "pallet-shared-storage/runtime-benchmarks", + "pallet-positive-externality/runtime-benchmarks", + "pallet-department-funding/runtime-benchmarks", + "pallet-project-tips/runtime-benchmarks", +] + +try-runtime = [ + "pallet-template/try-runtime", + "pallet-sortition-sum-game/try-runtime", + "pallet-schelling-game-shared/try-runtime", + "pallet-profile-validation/try-runtime", + "pallet-shared-storage/try-runtime", + "pallet-positive-externality/try-runtime", + "pallet-department-funding/try-runtime", + "pallet-project-tips/try-runtime", +] + +``` + +### **runtime-templates/simple/src/lib.rs** + +```rust + +impl pallet_template::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_template::weights::SubstrateWeight; +} + +impl pallet_insecure_randomness_collective_flip::Config for Runtime {} + + +impl pallet_sortition_sum_game::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_sortition_sum_game::weights::SubstrateWeight; +} + +impl pallet_schelling_game_shared::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_schelling_game_shared::weights::SubstrateWeight; + type Currency = Balances; + type RandomnessSource = RandomnessCollectiveFlip; + type Slash = (); + type Reward = (); + type SortitionSumGameSource = SortitionSumGame; +} + +impl pallet_profile_validation::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_profile_validation::weights::SubstrateWeight; + type Currency = Balances; + type SchellingGameSharedSource = SchellingGameShared; + type Slash = (); + type Reward = (); +} + + +impl pallet_shared_storage::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_shared_storage::weights::SubstrateWeight; +} + +impl pallet_positive_externality::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_positive_externality::weights::SubstrateWeight; + type SharedStorageSource = SharedStorage; + type Currency = Balances; + type SchellingGameSharedSource = SchellingGameShared; +} + +impl pallet_department_funding::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_department_funding::weights::SubstrateWeight; + type SharedStorageSource = SharedStorage; + type Currency = Balances; + type SchellingGameSharedSource = SchellingGameShared; +} + +impl pallet_project_tips::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_project_tips::weights::SubstrateWeight; + type SharedStorageSource = SharedStorage; + type Currency = Balances; + type Reward = (); + type SchellingGameSharedSource = SchellingGameShared; +} + + +construct_runtime!( + pub enum Runtime + { + TemplateModule: pallet_template = 200, + SortitionSumGame: pallet_sortition_sum_game = 201, + SchellingGameShared: pallet_schelling_game_shared = 202, + ProfileValidation: pallet_profile_validation = 203, + SharedStorage: pallet_shared_storage = 204, + PositiveExternality: pallet_positive_externality = 205, + DepartmentFunding: pallet_department_funding = 206, + ProjectTips: pallet_project_tips = 207, + RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip = 208, + } +); + +``` + +### **nodes/simple/src/chain_spec.rs** + +```rust +fn testnet_genesis( + endowed_accounts: Vec, + id: ParaId, + root_key: AccountId, +) -> serde_json::Value { + let g = container_chain_template_simple_runtime::RuntimeGenesisConfig { + shared_storage: Default::default(), + }; +``` + +### **nodes/simple/src/Cargo.toml** + +```toml +pallet-shared-storage = { workspace = true } +``` diff --git a/media/tanssi.png b/media/tanssi.png new file mode 100644 index 0000000..78ed939 Binary files /dev/null and b/media/tanssi.png differ diff --git a/node/Cargo.toml b/node/Cargo.toml index 223fb79..560c7a7 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,100 +1,138 @@ [package] -name = "node-template" -version = "4.0.0-dev" -description = "A fresh FRAME-based Substrate node, ready for hacking." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io/" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" +name = "tanssi-node" +authors = { workspace = true } build = "build.rs" +description = "Tanssi node implementation" +edition = "2021" +license = "GPL-3.0-only" +version = "0.7.0" -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[[bin]] -name = "node-template" +[lints] +workspace = true [dependencies] -clap = { version = "4.0.9", features = ["derive"] } -futures = { version = "0.3.21", features = ["thread-pool"]} -hex-literal = "0.4.1" - -sc-cli = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-executor = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-service = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-telemetry = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-keystore = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-transaction-pool = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-transaction-pool-api = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-consensus-aura = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-consensus-aura = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-consensus = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-consensus = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-consensus-grandpa = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-consensus-grandpa = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-client-api = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-timestamp = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-inherents = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-keyring = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -# These dependencies are used for the node template's RPCs -jsonrpsee = { version = "0.16.2", features = ["server"] } -sc-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-api = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-block-builder = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-basic-authorship = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -substrate-frame-rpc-system = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-transaction-payment-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -# These dependencies are used for runtime benchmarking -frame-benchmarking = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-benchmarking-cli = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -# Local Dependencies -node-template-runtime = { version = "4.0.0-dev", path = "../runtime/node-runtime" } - -# CLI-specific dependencies -try-runtime-cli = { version = "0.10.0-dev", optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -# New rpc -profile-validation-runtime-api = {path="../pallets/profile-validation/profile-validation-runtime-api", default-features = false} -profile-validation-rpc = { path="../pallets/profile-validation/profile-validation-rpc", default-features = false} - - -# Department funding rpc -department-funding-runtime-api = {path="../pallets/department-funding/department-funding-runtime-api", default-features = false} -department-funding-rpc= { path="../pallets/department-funding/department-funding-rpc", default-features = false} - -# Postive exterality rpc -positive-externality-runtime-api = {path="../pallets/positive-externality/positive-externality-runtime-api", default-features = false} -positive-externality-rpc= { path="../pallets/positive-externality/positive-externality-rpc", default-features = false} - -# Project tip rpc -project-tips-runtime-api = {path="../pallets/project-tips/project-tips-runtime-api", default-features = false} -project-tips-rpc= { path="../pallets/project-tips/project-tips-rpc", default-features = false} - - - +async-io = { workspace = true } +async-trait = { workspace = true } +clap = { workspace = true, features = [ "derive" ] } +exit-future = { workspace = true } +flume = { workspace = true } +futures = { workspace = true } +jsonrpsee = { workspace = true, features = [ "server" ] } +log = { workspace = true } +parity-scale-codec = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } +serde_json = { workspace = true } +tokio = { workspace = true } +tokio-util = { workspace = true } + +# Local +ccp-authorities-noting-inherent = { workspace = true, features = [ "std" ] } +dancebox-runtime = { workspace = true, features = [ "std" ] } +dp-slot-duration-runtime-api = { workspace = true } +flashbox-runtime = { workspace = true, features = [ "std" ] } +manual-xcm-rpc = { workspace = true } +node-common = { workspace = true } +pallet-author-noting-runtime-api = { workspace = true, features = [ "std" ] } +pallet-collator-assignment-runtime-api = { workspace = true, features = [ "std" ] } +pallet-configuration = { workspace = true, features = [ "std" ] } +pallet-registrar-runtime-api = { workspace = true, features = [ "std" ] } +services-payment-rpc = { workspace = true } +stream-payment-rpc = { workspace = true } +tp-author-noting-inherent = { workspace = true, features = [ "std" ] } +tp-container-chain-genesis-data = { workspace = true, features = [ "json", "std" ] } + +dc-orchestrator-chain-interface = { workspace = true } +tc-consensus = { workspace = true } + + +# Nimbus +nimbus-consensus = { workspace = true } +nimbus-primitives = { workspace = true, features = [ "std" ] } + +# Substrate +frame-benchmarking = { workspace = true } +frame-benchmarking-cli = { workspace = true } +sc-basic-authorship = { workspace = true } +sc-chain-spec = { workspace = true } +sc-cli = { workspace = true } +sc-client-api = { workspace = true } +sc-consensus = { workspace = true } +sc-consensus-aura = { workspace = true } +sc-consensus-manual-seal = { workspace = true } +sc-executor = { workspace = true } +sc-network = { workspace = true } +sc-network-common = { workspace = true } +sc-network-sync = { workspace = true } +sc-offchain = { workspace = true } +sc-rpc = { workspace = true } +sc-service = { workspace = true } +sc-sysinfo = { workspace = true } +sc-telemetry = { workspace = true } +sc-tracing = { workspace = true } +sc-transaction-pool = { workspace = true } +sc-transaction-pool-api = { workspace = true } +sp-api = { workspace = true, features = [ "std" ] } +sp-block-builder = { workspace = true } +sp-blockchain = { workspace = true } +sp-consensus = { workspace = true } + +sp-consensus-aura = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true, features = [ "std" ] } +sp-inherents = { workspace = true, features = [ "std" ] } +sp-io = { workspace = true, features = [ "std" ] } +sp-keystore = { workspace = true, features = [ "std" ] } +sp-offchain = { workspace = true, features = [ "std" ] } +sp-runtime = { workspace = true, features = [ "std" ] } +sp-session = { workspace = true, features = [ "std" ] } +sp-state-machine = { workspace = true, features = [ "std" ] } +sp-timestamp = { workspace = true, features = [ "std" ] } + +sp-transaction-pool = { workspace = true } +substrate-frame-rpc-system = { workspace = true } +substrate-prometheus-endpoint = { workspace = true } +try-runtime-cli = { workspace = true, optional = true } + +# Polkadot +polkadot-cli = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-primitives = { workspace = true } +polkadot-service = { workspace = true } + +# Cumulus +cumulus-client-cli = { workspace = true } +cumulus-client-collator = { workspace = true } +cumulus-client-consensus-aura = { workspace = true } +cumulus-client-consensus-common = { workspace = true } +cumulus-client-consensus-proposer = { workspace = true } +cumulus-client-network = { workspace = true } +cumulus-client-parachain-inherent = { workspace = true } +cumulus-client-pov-recovery = { workspace = true } +cumulus-client-service = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-relay-chain-interface = { workspace = true } +[dev-dependencies] +sp-panic-handler = { workspace = true } [build-dependencies] -substrate-build-script-utils = { version = "3.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +substrate-build-script-utils = { workspace = true } [features] default = [] -# Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ - "node-template-runtime/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "dancebox-runtime/runtime-benchmarks", + "flashbox-runtime/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "nimbus-primitives/runtime-benchmarks", + "pallet-configuration/runtime-benchmarks", + "polkadot-cli/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-service/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] -# Enable features that allow the runtime to be tried and debugged. Name might be subject to change -# in the near future. -try-runtime = ["node-template-runtime/try-runtime", "try-runtime-cli/try-runtime"] +try-runtime = [ "dancebox-runtime/try-runtime", "flashbox-runtime/try-runtime", "nimbus-primitives/try-runtime", "pallet-configuration/try-runtime", "polkadot-cli/try-runtime", "polkadot-service/try-runtime", "sp-runtime/try-runtime", "try-runtime-cli/try-runtime" ] + +fast-runtime = [ "dancebox-runtime/fast-runtime", "flashbox-runtime/fast-runtime" ] diff --git a/node/build.rs b/node/build.rs index e3bfe31..74ead7d 100644 --- a/node/build.rs +++ b/node/build.rs @@ -1,7 +1,23 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; fn main() { - generate_cargo_keys(); + generate_cargo_keys(); - rerun_if_git_head_changed(); + rerun_if_git_head_changed(); } diff --git a/node/src/benchmarking.rs b/node/src/benchmarking.rs deleted file mode 100644 index 37e0e46..0000000 --- a/node/src/benchmarking.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! Setup code for [`super::command`] which would otherwise bloat that module. -//! -//! Should only be used for benchmarking as it may break in other contexts. - -use crate::service::FullClient; - -use node_template_runtime as runtime; -use runtime::{AccountId, Balance, BalancesCall, SystemCall}; -use sc_cli::Result; -use sc_client_api::BlockBackend; -use sp_core::{Encode, Pair}; -use sp_inherents::{InherentData, InherentDataProvider}; -use sp_keyring::Sr25519Keyring; -use sp_runtime::{OpaqueExtrinsic, SaturatedConversion}; - -use std::{sync::Arc, time::Duration}; - -/// Generates extrinsics for the `benchmark overhead` command. -/// -/// Note: Should only be used for benchmarking. -pub struct RemarkBuilder { - client: Arc, -} - -impl RemarkBuilder { - /// Creates a new [`Self`] from the given client. - pub fn new(client: Arc) -> Self { - Self { client } - } -} - -impl frame_benchmarking_cli::ExtrinsicBuilder for RemarkBuilder { - fn pallet(&self) -> &str { - "system" - } - - fn extrinsic(&self) -> &str { - "remark" - } - - fn build(&self, nonce: u32) -> std::result::Result { - let acc = Sr25519Keyring::Bob.pair(); - let extrinsic: OpaqueExtrinsic = create_benchmark_extrinsic( - self.client.as_ref(), - acc, - SystemCall::remark { remark: vec![] }.into(), - nonce, - ) - .into(); - - Ok(extrinsic) - } -} - -/// Generates `Balances::TransferKeepAlive` extrinsics for the benchmarks. -/// -/// Note: Should only be used for benchmarking. -pub struct TransferKeepAliveBuilder { - client: Arc, - dest: AccountId, - value: Balance, -} - -impl TransferKeepAliveBuilder { - /// Creates a new [`Self`] from the given client. - pub fn new(client: Arc, dest: AccountId, value: Balance) -> Self { - Self { client, dest, value } - } -} - -impl frame_benchmarking_cli::ExtrinsicBuilder for TransferKeepAliveBuilder { - fn pallet(&self) -> &str { - "balances" - } - - fn extrinsic(&self) -> &str { - "transfer_keep_alive" - } - - fn build(&self, nonce: u32) -> std::result::Result { - let acc = Sr25519Keyring::Bob.pair(); - let extrinsic: OpaqueExtrinsic = create_benchmark_extrinsic( - self.client.as_ref(), - acc, - BalancesCall::transfer_keep_alive { - dest: self.dest.clone().into(), - value: self.value.into(), - } - .into(), - nonce, - ) - .into(); - - Ok(extrinsic) - } -} - -/// Create a transaction using the given `call`. -/// -/// Note: Should only be used for benchmarking. -pub fn create_benchmark_extrinsic( - client: &FullClient, - sender: sp_core::sr25519::Pair, - call: runtime::RuntimeCall, - nonce: u32, -) -> runtime::UncheckedExtrinsic { - let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); - let best_hash = client.chain_info().best_hash; - let best_block = client.chain_info().best_number; - - let period = runtime::BlockHashCount::get() - .checked_next_power_of_two() - .map(|c| c / 2) - .unwrap_or(2) as u64; - let extra: runtime::SignedExtra = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(sp_runtime::generic::Era::mortal( - period, - best_block.saturated_into(), - )), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(0), - ); - - let raw_payload = runtime::SignedPayload::from_raw( - call.clone(), - extra.clone(), - ( - (), - runtime::VERSION.spec_version, - runtime::VERSION.transaction_version, - genesis_hash, - best_hash, - (), - (), - (), - ), - ); - let signature = raw_payload.using_encoded(|e| sender.sign(e)); - - runtime::UncheckedExtrinsic::new_signed( - call.clone(), - sp_runtime::AccountId32::from(sender.public()).into(), - runtime::Signature::Sr25519(signature.clone()), - extra.clone(), - ) -} - -/// Generates inherent data for the `benchmark overhead` command. -/// -/// Note: Should only be used for benchmarking. -pub fn inherent_benchmark_data() -> Result { - let mut inherent_data = InherentData::new(); - let d = Duration::from_millis(0); - let timestamp = sp_timestamp::InherentDataProvider::new(d.into()); - - futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data)) - .map_err(|e| format!("creating inherent data: {:?}", e))?; - Ok(inherent_data) -} diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs deleted file mode 100644 index b416780..0000000 --- a/node/src/chain_spec.rs +++ /dev/null @@ -1,180 +0,0 @@ -use hex_literal::hex; -use node_template_runtime::{ - AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, SharedStorageConfig, - Signature, SudoConfig, SystemConfig, WASM_BINARY, -}; -use sc_service::ChainType; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; - -// The URL for the telemetry server. -// const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; - -/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. -pub type ChainSpec = sc_service::GenericChainSpec; - -/// Generate a crypto pair from seed. -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - -type AccountPublic = ::Signer; - -/// Generate an account ID from seed. -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Generate an Aura authority key. -pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { - (get_from_seed::(s), get_from_seed::(s)) -} - -pub fn development_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; - - Ok(ChainSpec::from_genesis( - // Name - "Development", - // ID - "dev", - ChainType::Development, - move || { - testnet_genesis( - wasm_binary, - // Initial PoA authorities - vec![authority_keys_from_seed("Alice")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - hex!("2e778beae3cc11fd7ea694f4ff8b54922d67e0599c356f393277ed9711d6364b").into(), - hex!("2e1c14cd13a2b090a62203809d8ce3eaac7417a4a0272438568eb04cae330669").into(), - hex!("ba0ce278d82ef9a686cb60a801125a8d11b32caa2456ebdcfe7ff687bb9bf540").into(), - hex!("600f10bdbf233ac6614eea62ae45d269b43c759e4ddf0bc1a70ffcbc95499c6c").into(), - hex!("c2da35a7aed402249295971abe8f10e0b03d861a0571e56115bcc6f8828dd939").into(), - hex!("186863b612097dec4ce7b9772381935baa7fc6dc7c44695f0384174f1b131156").into(), - hex!("70c3f87a26743fed9194f8fc67bfdd9a211f3b00f5c80459107022d096dbf928").into(), - hex!("cab4abef5dda97cc98eb0f3a5e0329bd2c1b892b5f442021a634c7e79e6f6e29").into(), - hex!("ac926b4e81989ca51c9ac6f0ef9c7db08d5334bb0a5c3b0194bf92d215b50f3f").into(), - hex!("186c72f04de9c1a74cee6836c08b6d56a88e90ab5a6127693a55379e8e03d919").into(), - hex!("b02de28c52fe59f9a3d8779cd8c6ee7439cba45e48e7ee582f5cc939c7b5946c").into(), - ], - true, - ) - }, - // Bootnodes - vec![], - // Telemetry - None, - // Protocol ID - None, - None, - // Properties - None, - // Extensions - None, - )) -} - -pub fn local_testnet_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; - - Ok(ChainSpec::from_genesis( - // Name - "Local Testnet", - // ID - "local_testnet", - ChainType::Local, - move || { - testnet_genesis( - wasm_binary, - // Initial PoA authorities - vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - hex!("2e778beae3cc11fd7ea694f4ff8b54922d67e0599c356f393277ed9711d6364b").into(), - hex!("2e1c14cd13a2b090a62203809d8ce3eaac7417a4a0272438568eb04cae330669").into(), - hex!("ba0ce278d82ef9a686cb60a801125a8d11b32caa2456ebdcfe7ff687bb9bf540").into(), - hex!("600f10bdbf233ac6614eea62ae45d269b43c759e4ddf0bc1a70ffcbc95499c6c").into(), - hex!("c2da35a7aed402249295971abe8f10e0b03d861a0571e56115bcc6f8828dd939").into(), - hex!("186863b612097dec4ce7b9772381935baa7fc6dc7c44695f0384174f1b131156").into(), - hex!("70c3f87a26743fed9194f8fc67bfdd9a211f3b00f5c80459107022d096dbf928").into(), - hex!("cab4abef5dda97cc98eb0f3a5e0329bd2c1b892b5f442021a634c7e79e6f6e29").into(), - hex!("ac926b4e81989ca51c9ac6f0ef9c7db08d5334bb0a5c3b0194bf92d215b50f3f").into(), - hex!("186c72f04de9c1a74cee6836c08b6d56a88e90ab5a6127693a55379e8e03d919").into(), - hex!("b02de28c52fe59f9a3d8779cd8c6ee7439cba45e48e7ee582f5cc939c7b5946c").into(), - ], - true, - ) - }, - // Bootnodes - vec![], - // Telemetry - None, - // Protocol ID - None, - // Properties - None, - None, - // Extensions - None, - )) -} - -/// Configure initial storage state for FRAME modules. -fn testnet_genesis( - wasm_binary: &[u8], - initial_authorities: Vec<(AuraId, GrandpaId)>, - root_key: AccountId, - endowed_accounts: Vec, - _enable_println: bool, -) -> GenesisConfig { - GenesisConfig { - system: SystemConfig { - // Add Wasm runtime to storage. - code: wasm_binary.to_vec(), - }, - balances: BalancesConfig { - // Configure endowed accounts with initial balance of 1 << 60. - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), - }, - aura: AuraConfig { - authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), - }, - grandpa: GrandpaConfig { - authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), - }, - sudo: SudoConfig { - // Assign network admin rights. - key: Some(root_key), - }, - transaction_payment: Default::default(), - shared_storage: SharedStorageConfig { approved_citizen_address: endowed_accounts }, - } -} diff --git a/node/src/chain_spec/dancebox.rs b/node/src/chain_spec/dancebox.rs new file mode 100644 index 0000000..1d1c926 --- /dev/null +++ b/node/src/chain_spec/dancebox.rs @@ -0,0 +1,290 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::chain_spec::{ + account_ids, get_account_id_from_seed, invulnerables_from_seeds, Extensions, + }, + cumulus_primitives_core::ParaId, + dancebox_runtime::{ + prod_or_fast, AccountId, DataPreserversConfig, MaintenanceModeConfig, MigrationsConfig, + PolkadotXcmConfig, RegistrarConfig, ServicesPaymentConfig, SudoConfig, + }, + nimbus_primitives::NimbusId, + pallet_configuration::HostConfiguration, + sc_service::ChainType, + sp_core::sr25519, + sp_runtime::{traits::Get, Perbill}, + tp_container_chain_genesis_data::{ + json::container_chain_genesis_data_from_path, ContainerChainGenesisData, + }, +}; + +/// Specialized `ChainSpec` for the normal parachain runtime. +pub type ChainSpec = + sc_service::GenericChainSpec; + +/// Generate the session keys from individual elements. +/// +/// The input must be a tuple of individual keys (a single arg for now since we have just one key). +pub fn template_session_keys(keys: NimbusId) -> dancebox_runtime::SessionKeys { + dancebox_runtime::SessionKeys { nimbus: keys } +} + +pub fn development_config( + para_id: ParaId, + container_chains: Vec, + mock_container_chains: Vec, + invulnerables: Vec, +) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "DANCE".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), false.into()); + + ChainSpec::builder( + dancebox_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name("Dancebox Development Testnet") + .with_id("dancebox_dev") + .with_chain_type(ChainType::Development) + .with_genesis_config(testnet_genesis( + // initial collators. + invulnerables_from_seeds(invulnerables.iter()), + account_ids(&[ + "Alice", + "Bob", + "Charlie", + "Dave", + "Eve", + "Ferdie", + "Alice//stash", + "Bob//stash", + "Charlie//stash", + "Dave//stash", + "Eve//stash", + "Ferdie//stash", + ]), + para_id, + get_account_id_from_seed::("Alice"), + &container_chains, + &mock_container_chains, + pallet_configuration::GenesisConfig { + config: HostConfiguration { + max_collators: 100u32, + min_orchestrator_collators: 1u32, + max_orchestrator_collators: 1u32, + collators_per_container: 2u32, + full_rotation_period: prod_or_fast!(24u32, 5u32), + collators_per_parathread: 1, + parathreads_per_collator: 1, + target_container_chain_fullness: Perbill::from_percent(80), + }, + ..Default::default() + }, + )) + .with_properties(properties) + .build() +} + +pub fn local_dancebox_config( + para_id: ParaId, + container_chains: Vec, + mock_container_chains: Vec, + invulnerables: Vec, +) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "DANCE".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), false.into()); + + ChainSpec::builder( + dancebox_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name("Dancebox Local Testnet") + .with_id("dancebox_local") + .with_chain_type(ChainType::Local) + .with_genesis_config(testnet_genesis( + // initial collators. + invulnerables_from_seeds(invulnerables.iter()), + account_ids(&[ + "Alice", + "Bob", + "Charlie", + "Dave", + "Eve", + "Ferdie", + "Alice//stash", + "Bob//stash", + "Charlie//stash", + "Dave//stash", + "Eve//stash", + "Ferdie//stash", + ]), + para_id, + get_account_id_from_seed::("Alice"), + &container_chains, + &mock_container_chains, + pallet_configuration::GenesisConfig { + config: HostConfiguration { + max_collators: 100u32, + min_orchestrator_collators: 2u32, + max_orchestrator_collators: 5u32, + collators_per_container: 2u32, + full_rotation_period: prod_or_fast!(24u32, 5u32), + collators_per_parathread: 1, + parathreads_per_collator: 1, + target_container_chain_fullness: Perbill::from_percent(80), + }, + ..Default::default() + }, + )) + .with_properties(properties) + .with_protocol_id("orchestrator") + .build() +} + +fn testnet_genesis( + invulnerables: Vec<(AccountId, NimbusId)>, + endowed_accounts: Vec, + id: ParaId, + root_key: AccountId, + container_chains: &[String], + mock_container_chains: &[ParaId], + configuration: pallet_configuration::GenesisConfig, +) -> serde_json::Value { + let para_ids: Vec<_> = container_chains + .iter() + .map(|x| { + container_chain_genesis_data_from_path(x).unwrap_or_else(|e| { + panic!( + "Failed to build genesis data for container chain {:?}: {}", + x, e + ) + }) + }) + .chain( + mock_container_chains + .iter() + .map(|x| (*x, mock_container_chain_genesis_data(*x), vec![])), + ) + .collect(); + // Assign 1000 block credits to all container chains registered in genesis + // Assign 100 collator assignment credits to all container chains registered in genesis + let para_id_credits: Vec<_> = para_ids + .iter() + .map(|(para_id, _genesis_data, _boot_nodes)| (*para_id, 1000, 100).into()) + .collect(); + let para_id_boot_nodes: Vec<_> = para_ids + .iter() + .map(|(para_id, _genesis_data, boot_nodes)| (*para_id, boot_nodes.clone())) + .collect(); + let para_ids: Vec<_> = para_ids + .into_iter() + .map(|(para_id, genesis_data, _boot_nodes)| (para_id, genesis_data)) + .collect(); + + let accounts_with_ed = vec![ + dancebox_runtime::StakingAccount::get(), + dancebox_runtime::ParachainBondAccount::get(), + dancebox_runtime::PendingRewardsAccount::get(), + ]; + let g = dancebox_runtime::RuntimeGenesisConfig { + system: Default::default(), + balances: dancebox_runtime::BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1 << 60)) + .chain( + accounts_with_ed + .iter() + .cloned() + .map(|k| (k, dancebox_runtime::EXISTENTIAL_DEPOSIT)), + ) + .collect(), + }, + parachain_info: dancebox_runtime::ParachainInfoConfig { + parachain_id: id, + ..Default::default() + }, + invulnerables: dancebox_runtime::InvulnerablesConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + }, + session: dancebox_runtime::SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + template_session_keys(aura), // session keys + ) + }) + .collect(), + }, + parachain_system: Default::default(), + configuration, + data_preservers: DataPreserversConfig { + para_id_boot_nodes, + ..Default::default() + }, + registrar: RegistrarConfig { para_ids }, + services_payment: ServicesPaymentConfig { para_id_credits }, + sudo: SudoConfig { + key: Some(root_key), + }, + migrations: MigrationsConfig { + ..Default::default() + }, + maintenance_mode: MaintenanceModeConfig { + start_in_maintenance_mode: false, + ..Default::default() + }, + // This should initialize it to whatever we have set in the pallet + polkadot_xcm: PolkadotXcmConfig::default(), + transaction_payment: Default::default(), + tx_pause: Default::default(), + treasury: Default::default(), + }; + + serde_json::to_value(g).unwrap() +} + +fn mock_container_chain_genesis_data>( + para_id: ParaId, +) -> ContainerChainGenesisData { + ContainerChainGenesisData { + storage: vec![], + name: format!("Container Chain {}", para_id).into(), + id: format!("container-chain-{}", para_id).into(), + fork_id: None, + extensions: vec![], + properties: Default::default(), + } +} diff --git a/node/src/chain_spec/flashbox.rs b/node/src/chain_spec/flashbox.rs new file mode 100644 index 0000000..4859355 --- /dev/null +++ b/node/src/chain_spec/flashbox.rs @@ -0,0 +1,288 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::chain_spec::{ + account_ids, get_account_id_from_seed, invulnerables_from_seeds, Extensions, + }, + cumulus_primitives_core::ParaId, + flashbox_runtime::{ + AccountId, DataPreserversConfig, MaintenanceModeConfig, MigrationsConfig, RegistrarConfig, + ServicesPaymentConfig, SudoConfig, + }, + nimbus_primitives::NimbusId, + pallet_configuration::HostConfiguration, + sc_service::ChainType, + sp_core::sr25519, + sp_runtime::{traits::Get, Perbill}, + tp_container_chain_genesis_data::{ + json::container_chain_genesis_data_from_path, ContainerChainGenesisData, + }, +}; + +/// Specialized `ChainSpec` for the normal parachain runtime. +pub type ChainSpec = + sc_service::GenericChainSpec; + +/// Generate the session keys from individual elements. +/// +/// The input must be a tuple of individual keys (a single arg for now since we have just one key). +pub fn template_session_keys(keys: NimbusId) -> flashbox_runtime::SessionKeys { + flashbox_runtime::SessionKeys { nimbus: keys } +} + +pub fn development_config( + para_id: ParaId, + container_chains: Vec, + mock_container_chains: Vec, + invulnerables: Vec, +) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "FLASH".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), false.into()); + + ChainSpec::builder( + flashbox_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name("Flashbox Development Testnet") + .with_id("flashbox_dev") + .with_chain_type(ChainType::Development) + .with_genesis_config(testnet_genesis( + // initial collators. + invulnerables_from_seeds(invulnerables.iter()), + account_ids(&[ + "Alice", + "Bob", + "Charlie", + "Dave", + "Eve", + "Ferdie", + "Alice//stash", + "Bob//stash", + "Charlie//stash", + "Dave//stash", + "Eve//stash", + "Ferdie//stash", + ]), + para_id, + get_account_id_from_seed::("Alice"), + &container_chains, + &mock_container_chains, + pallet_configuration::GenesisConfig { + config: HostConfiguration { + max_collators: 100u32, + min_orchestrator_collators: 1u32, + max_orchestrator_collators: 1u32, + collators_per_container: 2u32, + full_rotation_period: 0, + collators_per_parathread: 1, + parathreads_per_collator: 1, + target_container_chain_fullness: Perbill::from_percent(80), + }, + ..Default::default() + }, + )) + .with_properties(properties) + .build() +} + +pub fn local_flashbox_config( + para_id: ParaId, + container_chains: Vec, + mock_container_chains: Vec, + invulnerables: Vec, +) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "FLASH".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), 42.into()); + properties.insert("isEthereum".into(), false.into()); + + ChainSpec::builder( + flashbox_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: para_id.into(), + }, + ) + .with_name("Flashbox Local Testnet") + .with_id("flashbox_local") + .with_chain_type(ChainType::Local) + .with_genesis_config(testnet_genesis( + // initial collators. + invulnerables_from_seeds(invulnerables.iter()), + account_ids(&[ + "Alice", + "Bob", + "Charlie", + "Dave", + "Eve", + "Ferdie", + "Alice//stash", + "Bob//stash", + "Charlie//stash", + "Dave//stash", + "Eve//stash", + "Ferdie//stash", + ]), + para_id, + get_account_id_from_seed::("Alice"), + &container_chains, + &mock_container_chains, + pallet_configuration::GenesisConfig { + config: HostConfiguration { + max_collators: 100u32, + min_orchestrator_collators: 2u32, + max_orchestrator_collators: 5u32, + collators_per_container: 2u32, + full_rotation_period: 0, + collators_per_parathread: 1, + parathreads_per_collator: 1, + target_container_chain_fullness: Perbill::from_percent(80), + }, + ..Default::default() + }, + )) + .with_properties(properties) + .with_protocol_id("orchestrator") + .build() +} + +fn testnet_genesis( + invulnerables: Vec<(AccountId, NimbusId)>, + endowed_accounts: Vec, + id: ParaId, + root_key: AccountId, + container_chains: &[String], + mock_container_chains: &[ParaId], + configuration: pallet_configuration::GenesisConfig, +) -> serde_json::Value { + let para_ids: Vec<_> = container_chains + .iter() + .map(|x| { + container_chain_genesis_data_from_path(x).unwrap_or_else(|e| { + panic!( + "Failed to build genesis data for container chain {:?}: {}", + x, e + ) + }) + }) + .chain( + mock_container_chains + .iter() + .map(|x| (*x, mock_container_chain_genesis_data(*x), vec![])), + ) + .collect(); + // Assign 1000 block credits to all container chains registered in genesis + // Assign 100 collator assignment credits to all container chains registered in genesis + let para_id_credits: Vec<_> = para_ids + .iter() + .map(|(para_id, _genesis_data, _boot_nodes)| (*para_id, 1000, 100).into()) + .collect(); + let para_id_boot_nodes: Vec<_> = para_ids + .iter() + .map(|(para_id, _genesis_data, boot_nodes)| (*para_id, boot_nodes.clone())) + .collect(); + let para_ids: Vec<_> = para_ids + .into_iter() + .map(|(para_id, genesis_data, _boot_nodes)| (para_id, genesis_data)) + .collect(); + + let accounts_with_ed = vec![ + flashbox_runtime::StakingAccount::get(), + flashbox_runtime::ParachainBondAccount::get(), + flashbox_runtime::PendingRewardsAccount::get(), + ]; + let g = flashbox_runtime::RuntimeGenesisConfig { + system: Default::default(), + balances: flashbox_runtime::BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1 << 60)) + .chain( + accounts_with_ed + .iter() + .cloned() + .map(|k| (k, flashbox_runtime::EXISTENTIAL_DEPOSIT)), + ) + .collect(), + }, + parachain_info: flashbox_runtime::ParachainInfoConfig { + parachain_id: id, + ..Default::default() + }, + invulnerables: flashbox_runtime::InvulnerablesConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + }, + session: flashbox_runtime::SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + template_session_keys(aura), // session keys + ) + }) + .collect(), + }, + parachain_system: Default::default(), + configuration, + data_preservers: DataPreserversConfig { + para_id_boot_nodes, + ..Default::default() + }, + registrar: RegistrarConfig { para_ids }, + services_payment: ServicesPaymentConfig { para_id_credits }, + sudo: SudoConfig { + key: Some(root_key), + }, + migrations: MigrationsConfig { + ..Default::default() + }, + maintenance_mode: MaintenanceModeConfig { + start_in_maintenance_mode: false, + ..Default::default() + }, + transaction_payment: Default::default(), + tx_pause: Default::default(), + treasury: Default::default(), + }; + + serde_json::to_value(g).unwrap() +} + +fn mock_container_chain_genesis_data>( + para_id: ParaId, +) -> ContainerChainGenesisData { + ContainerChainGenesisData { + storage: vec![], + name: format!("Container Chain {}", para_id).into(), + id: format!("container-chain-{}", para_id).into(), + fork_id: None, + extensions: vec![], + properties: Default::default(), + } +} diff --git a/node/src/chain_spec/mod.rs b/node/src/chain_spec/mod.rs new file mode 100644 index 0000000..6444596 --- /dev/null +++ b/node/src/chain_spec/mod.rs @@ -0,0 +1,132 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + dancebox_runtime::{AccountId, Signature}, + nimbus_primitives::NimbusId, + sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}, + serde::{Deserialize, Serialize}, + sp_core::{sr25519, Pair, Public}, + sp_runtime::traits::{IdentifyAccount, Verify}, + std::collections::BTreeMap, +}; + +pub mod dancebox; +pub mod flashbox; + +/// Specialized `ChainSpec` for container chains that only allows raw genesis format. +pub type RawChainSpec = sc_service::GenericChainSpec; + +/// Helper type that implements the traits needed to be used as a "GenesisConfig", +/// but whose implementation panics because we only expect it to be used with raw ChainSpecs, +/// so it will never be serialized or deserialized. +/// This is because container chains must use raw chain spec files where the "genesis" +/// field only has one field: "raw". +pub struct RawGenesisConfig { + pub storage_raw: BTreeMap, Vec>, +} + +impl Serialize for RawGenesisConfig { + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + panic!("RawGenesisConfigDummy should never be serialized") + } +} + +impl<'de> Deserialize<'de> for RawGenesisConfig { + fn deserialize(_deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + panic!("Attempted to read a non-raw ContainerChain ChainSpec.\nHelp: add `--raw` flag to `build-spec` command to generate a raw chain spec") + } +} + +impl sp_runtime::BuildStorage for RawGenesisConfig { + fn assimilate_storage(&self, storage: &mut sp_core::storage::Storage) -> Result<(), String> { + storage + .top + .extend(self.storage_raw.iter().map(|(k, v)| (k.clone(), v.clone()))); + + Ok(()) + } +} + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +/// The extensions for the [`ChainSpec`]. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] +#[serde(deny_unknown_fields)] +pub struct Extensions { + /// The relay chain of the Parachain. + pub relay_chain: String, + /// The id of the Parachain. + pub para_id: u32, +} + +impl Extensions { + /// Try to get the extension from the given `ChainSpec`. + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +type AccountPublic = ::Signer; + +/// Generate collator keys from seed. +/// +/// This function's return type must always match the session keys of the chain in tuple format. +pub fn get_collator_keys_from_seed(seed: &str) -> NimbusId { + get_from_seed::(seed) +} + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Helper function to turn a list of names into a list of `(AccountId, NimbusId)` +pub fn invulnerables_from_seeds, I: Iterator>( + names: I, +) -> Vec<(AccountId, NimbusId)> { + names + .map(|name| { + let name = name.as_ref(); + ( + get_account_id_from_seed::(name), + get_collator_keys_from_seed(name), + ) + }) + .collect() +} + +/// Helper function to turn a list of names into a list of `AccountId` +pub fn account_ids(names: &[&str]) -> Vec { + names + .iter() + .map(|name| get_account_id_from_seed::(name)) + .collect() +} diff --git a/node/src/cli.rs b/node/src/cli.rs index dd61047..b4f1399 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -1,53 +1,397 @@ -use sc_cli::RunCmd; +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::chain_spec::RawGenesisConfig, + node_common::service::Sealing, + pallet_registrar_runtime_api::ContainerChainGenesisData, + sc_chain_spec::ChainSpec, + sc_cli::{CliConfiguration, NodeKeyParams, SharedParams}, + sc_network::config::MultiaddrWithPeerId, + sp_runtime::{traits::Get, Storage}, + std::{collections::BTreeMap, path::PathBuf}, + tp_container_chain_genesis_data::json::properties_to_map, +}; + +/// Sub-commands supported by the collator. +#[derive(Debug, clap::Subcommand)] +#[allow(clippy::large_enum_variant)] +pub enum Subcommand { + /// Build a chain specification. + BuildSpec(BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Remove the whole chain. + PurgeChain(cumulus_client_cli::PurgeChainCmd), + + /// Export the genesis state of the parachain. + #[command(alias = "export-genesis-state")] + ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand), + + /// Export the genesis wasm of the parachain. + ExportGenesisWasm(ExportGenesisWasmCommand), + + /// Sub-commands concerned with benchmarking. + /// The pallet benchmarking moved to the `pallet` sub-command. + #[command(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), + + /// Try some testing command against a specified runtime state. + #[cfg(feature = "try-runtime")] + TryRuntime(try_runtime_cli::TryRuntimeCmd), + + /// Errors since the binary was not build with `--features try-runtime`. + #[cfg(not(feature = "try-runtime"))] + TryRuntime, + + /// Key management cli utilities + #[command(subcommand)] + Key(KeyCmd), + + /// Precompile the WASM runtime into native code + PrecompileWasm(sc_cli::PrecompileWasmCmd), +} + +/// The `build-spec` command used to build a specification. +#[derive(Debug, Clone, clap::Parser)] +pub struct BuildSpecCmd { + #[clap(flatten)] + pub base: sc_cli::BuildSpecCmd, + + /// Id of the parachain this spec is for. Note that this overrides the `--chain` param. + #[arg(long)] + pub parachain_id: Option, + + /// List of container chain chain spec paths to add to genesis. + #[arg(long)] + pub add_container_chain: Option>, + + /// List of container chain chain spec mocks to add to genesis. + #[arg(long)] + pub mock_container_chain: Option>, + + /// List of invulnerable collators to write to pallet_invulnerables genesis. + #[arg(long)] + pub invulnerable: Option>, +} + +impl CliConfiguration for BuildSpecCmd { + fn shared_params(&self) -> &SharedParams { + &self.base.shared_params + } + + fn node_key_params(&self) -> Option<&NodeKeyParams> { + Some(&self.base.node_key_params) + } +} + +/// Command for exporting the genesis wasm file. #[derive(Debug, clap::Parser)] -pub struct Cli { - #[command(subcommand)] - pub subcommand: Option, +pub struct ExportGenesisWasmCommand { + /// Output file name or stdout if unspecified. + pub output: Option, + + /// Write output in binary. Default is to write in hex. + #[arg(short, long)] + pub raw: bool, - #[clap(flatten)] - pub run: RunCmd, + /// The name of the chain for that the genesis wasm file should be exported. + #[arg(long)] + pub chain: Option, +} + +#[derive(Debug, clap::Parser)] +#[group(skip)] +pub struct RunCmd { + #[clap(flatten)] + pub base: cumulus_client_cli::RunCmd, + + /// Enable the development service to run without a backing relay chain + #[arg(long)] + pub dev_service: bool, + + /// When blocks should be sealed in the dev service. + /// + /// Options are "instant", "manual", or timer interval in milliseconds + #[arg(long, default_value = "instant")] + pub sealing: Sealing, + + /// Id of the parachain this collator collates for. + #[arg(long)] + pub parachain_id: Option, +} + +impl std::ops::Deref for RunCmd { + type Target = cumulus_client_cli::RunCmd; + + fn deref(&self) -> &Self::Target { + &self.base + } } #[derive(Debug, clap::Subcommand)] -pub enum Subcommand { - /// Key management cli utilities - #[command(subcommand)] - Key(sc_cli::KeySubcommand), +pub enum KeyCmd { + #[command(flatten)] + BaseCli(sc_cli::KeySubcommand), +} + +impl KeyCmd { + /// run the key subcommands + pub fn run(&self, cli: &C) -> Result<(), sc_cli::Error> { + match self { + KeyCmd::BaseCli(cmd) => cmd.run(cli), + } + } +} + +#[derive(Debug, clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[command(subcommand)] + pub subcommand: Option, + + #[command(flatten)] + pub run: RunCmd, + + /// Disable automatic hardware benchmarks. + /// + /// By default these benchmarks are automatically ran at startup and measure + /// the CPU speed, the memory bandwidth and the disk speed. + /// + /// The results are then printed out in the logs, and also sent as part of + /// telemetry, if telemetry is enabled. + #[arg(long)] + pub no_hardware_benchmarks: bool, + + /// Optional parachain id that should be used to build chain spec. + #[arg(long)] + pub para_id: Option, + + /// Relay chain arguments, optionally followed by "--" and orchestrator chain arguments + #[arg(raw = true)] + extra_args: Vec, +} + +impl Cli { + pub fn relaychain_args(&self) -> &[String] { + let (relay_chain_args, _) = self.split_extra_args_at_first_dashdash(); + + relay_chain_args + } + + pub fn container_chain_args(&self) -> &[String] { + let (_, container_chain_args) = self.split_extra_args_at_first_dashdash(); + + container_chain_args + } + + fn split_extra_args_at_first_dashdash(&self) -> (&[String], &[String]) { + let index_of_dashdash = self.extra_args.iter().position(|x| *x == "--"); - /// Build a chain specification. - BuildSpec(sc_cli::BuildSpecCmd), + if let Some(i) = index_of_dashdash { + let (container_chain_args, extra_extra) = self.extra_args.split_at(i); + (&extra_extra[1..], container_chain_args) + } else { + // Only relay chain args + (&self.extra_args, &[]) + } + } +} + +#[derive(Debug)] +pub struct RelayChainCli { + /// The actual relay chain cli object. + pub base: polkadot_cli::RunCmd, + + /// Optional chain id that should be passed to the relay chain. + pub chain_id: Option, + + /// The base path that should be used by the relay chain. + pub base_path: PathBuf, +} + +impl RelayChainCli { + /// Parse the relay chain CLI parameters using the para chain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + relay_chain_args: impl Iterator, + ) -> Self { + let extension = crate::chain_spec::Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + let base_path = para_config.base_path.path().join("polkadot"); + + Self { + base_path, + chain_id, + base: clap::Parser::parse_from(relay_chain_args), + } + } +} + +/// The `run` command used to run a container chain node. +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct ContainerChainRunCmd { + /// The cumulus RunCmd inherits from sc_cli's + #[command(flatten)] + pub base: sc_cli::RunCmd, + + /// Run node as collator. + /// + /// Note that this is the same as running with `--validator`. + #[arg(long, conflicts_with = "validator")] + pub collator: bool, + + /// Optional container chain para id that should be used to build chain spec. + #[arg(long)] + pub para_id: Option, + + /// Keep container-chain db after changing collator assignments + #[arg(long)] + pub keep_db: bool, +} + +#[derive(Debug)] +pub struct ContainerChainCli { + /// The actual container chain cli object. + pub base: ContainerChainRunCmd, + + /// The base path that should be used by the container chain. + pub base_path: PathBuf, + + /// The ChainSpecs that this struct can initialize. This starts empty and gets filled + /// by calling preload_chain_spec_file. + pub preloaded_chain_spec: Option>, +} + +impl Clone for ContainerChainCli { + fn clone(&self) -> Self { + Self { + base: self.base.clone(), + base_path: self.base_path.clone(), + preloaded_chain_spec: self.preloaded_chain_spec.as_ref().map(|x| x.cloned_box()), + } + } +} - /// Validate blocks. - CheckBlock(sc_cli::CheckBlockCmd), +impl ContainerChainCli { + /// Parse the container chain CLI parameters using the para chain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + container_chain_args: impl Iterator, + ) -> Self { + let base_path = para_config.base_path.path().join("containers"); - /// Export blocks. - ExportBlocks(sc_cli::ExportBlocksCmd), + Self { + base_path, + base: clap::Parser::parse_from(container_chain_args), + preloaded_chain_spec: None, + } + } - /// Export the state of a given block into a chain spec. - ExportState(sc_cli::ExportStateCmd), + pub fn chain_spec_from_genesis_data>( + para_id: u32, + genesis_data: ContainerChainGenesisData, + chain_type: sc_chain_spec::ChainType, + relay_chain: String, + boot_nodes: Vec, + ) -> Result { + let name = String::from_utf8(genesis_data.name).map_err(|_e| "Invalid name".to_string())?; + let id: String = + String::from_utf8(genesis_data.id).map_err(|_e| "Invalid id".to_string())?; + let storage_raw: BTreeMap<_, _> = + genesis_data.storage.into_iter().map(|x| x.into()).collect(); + let protocol_id = format!("container-chain-{}", para_id); + let properties = properties_to_map(&genesis_data.properties) + .map_err(|e| format!("Invalid properties: {}", e))?; + let extensions = crate::chain_spec::Extensions { + relay_chain, + para_id, + }; + let raw_genesis_config = RawGenesisConfig { + storage_raw: storage_raw.clone(), + }; - /// Import blocks. - ImportBlocks(sc_cli::ImportBlocksCmd), + let chain_spec = crate::chain_spec::RawChainSpec::builder( + // This code is not used, we override it in `set_storage` below + &[], + // TODO: what to do with extensions? We are hardcoding the relay_chain and the para_id, any + // other extensions are being ignored + extensions, + ) + .with_name(&name) + .with_id(&id) + .with_chain_type(chain_type) + .with_properties(properties) + .with_boot_nodes(boot_nodes) + .with_protocol_id(&protocol_id); - /// Remove the whole chain. - PurgeChain(sc_cli::PurgeChainCmd), + let chain_spec = if let Some(fork_id) = genesis_data.fork_id { + let fork_id_string = + String::from_utf8(fork_id).map_err(|_e| "Invalid fork_id".to_string())?; + chain_spec.with_fork_id(&fork_id_string) + } else { + chain_spec + }; - /// Revert the chain to a previous state. - Revert(sc_cli::RevertCmd), + let mut chain_spec = chain_spec.build(); - /// Sub-commands concerned with benchmarking. - #[command(subcommand)] - Benchmark(frame_benchmarking_cli::BenchmarkCmd), + chain_spec.set_storage(Storage { + top: raw_genesis_config.storage_raw, + children_default: Default::default(), + }); - /// Try some command against runtime state. - #[cfg(feature = "try-runtime")] - TryRuntime(try_runtime_cli::TryRuntimeCmd), + Ok(chain_spec) + } - /// Try some command against runtime state. Note: `try-runtime` feature must be enabled. - #[cfg(not(feature = "try-runtime"))] - TryRuntime, + pub fn preload_chain_spec_from_genesis_data>( + &mut self, + para_id: u32, + genesis_data: ContainerChainGenesisData, + chain_type: sc_chain_spec::ChainType, + relay_chain: String, + boot_nodes: Vec, + ) -> Result<(), String> { + let chain_spec = Self::chain_spec_from_genesis_data( + para_id, + genesis_data, + chain_type, + relay_chain, + boot_nodes, + )?; + self.preloaded_chain_spec = Some(Box::new(chain_spec)); - /// Db meta columns information. - ChainInfo(sc_cli::ChainInfoCmd), + Ok(()) + } } diff --git a/node/src/command.rs b/node/src/command.rs index c90feef..da38555 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -1,218 +1,766 @@ -use crate::{ - benchmarking::{inherent_benchmark_data, RemarkBuilder, TransferKeepAliveBuilder}, - chain_spec, - cli::{Cli, Subcommand}, - service, +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::{ + chain_spec, + cli::{Cli, ContainerChainCli, RelayChainCli, Subcommand}, + service::{self, IdentifyVariant, NodeConfig}, + }, + cumulus_client_cli::extract_genesis_wasm, + cumulus_primitives_core::ParaId, + dancebox_runtime::Block, + frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}, + log::{info, warn}, + node_common::{command::generate_genesis_block, service::NodeBuilderConfig as _}, + parity_scale_codec::Encode, + polkadot_service::WestendChainSpec, + sc_cli::{ + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, + NetworkParams, Result, SharedParams, SubstrateCli, + }, + sc_service::config::{BasePath, PrometheusConfig}, + sp_core::hexdisplay::HexDisplay, + sp_runtime::traits::{AccountIdConversion, Block as BlockT}, + std::{io::Write, net::SocketAddr}, }; -use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; -use node_template_runtime::{Block, EXISTENTIAL_DEPOSIT}; -use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; -use sc_service::PartialComponents; -use sp_keyring::Sr25519Keyring; -#[cfg(feature = "try-runtime")] -use try_runtime_cli::block_building_info::timestamp_with_aura_info; +fn load_spec( + id: &str, + para_id: Option, + container_chains: Vec, + mock_container_chains: Vec, + invulnerables: Option>, +) -> std::result::Result, String> { + let para_id: ParaId = para_id.unwrap_or(1000).into(); + let mock_container_chains: Vec = + mock_container_chains.iter().map(|&x| x.into()).collect(); + let invulnerables = invulnerables.unwrap_or(vec![ + "Alice".to_string(), + "Bob".to_string(), + "Charlie".to_string(), + "Dave".to_string(), + ]); + + Ok(match id { + "dev" | "dancebox-dev" | "dancebox_dev" => { + Box::new(chain_spec::dancebox::development_config( + para_id, + container_chains, + mock_container_chains, + invulnerables, + )) + } + "" | "dancebox-local" | "dancebox_local" => { + Box::new(chain_spec::dancebox::local_dancebox_config( + para_id, + container_chains, + mock_container_chains, + invulnerables, + )) + } + "dancebox" => Box::new(chain_spec::RawChainSpec::from_json_bytes( + &include_bytes!("../../specs/dancebox/dancebox-raw-specs.json")[..], + )?), + "flashbox-dev" | "flashbox_dev" => Box::new(chain_spec::flashbox::development_config( + para_id, + container_chains, + mock_container_chains, + invulnerables, + )), + "flashbox-local" | "flashbox_local" => { + Box::new(chain_spec::flashbox::local_flashbox_config( + para_id, + container_chains, + mock_container_chains, + invulnerables, + )) + } + path => Box::new(chain_spec::dancebox::ChainSpec::from_json_file( + std::path::PathBuf::from(path), + )?), + }) +} impl SubstrateCli for Cli { - fn impl_name() -> String { - "Substrate Node".into() - } - - fn impl_version() -> String { - env!("SUBSTRATE_CLI_IMPL_VERSION").into() - } - - fn description() -> String { - env!("CARGO_PKG_DESCRIPTION").into() - } - - fn author() -> String { - env!("CARGO_PKG_AUTHORS").into() - } - - fn support_url() -> String { - "support.anonymous.an".into() - } - - fn copyright_start_year() -> i32 { - 2017 - } - - fn load_spec(&self, id: &str) -> Result, String> { - Ok(match id { - "dev" => Box::new(chain_spec::development_config()?), - "" | "local" => Box::new(chain_spec::local_testnet_config()?), - path => { - Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?) - }, - }) - } + fn impl_name() -> String { + "Tanssi Collator".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } - fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { - &node_template_runtime::VERSION - } + fn description() -> String { + format!( + "Tanssi Collator\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/cumulus/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2020 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + load_spec(id, self.para_id, vec![], vec![2000, 2001], None) + } } -/// Parse and run command line arguments -pub fn run() -> sc_cli::Result<()> { - let cli = Cli::from_args(); - - match &cli.subcommand { - Some(Subcommand::Key(cmd)) => cmd.run(&cli), - Some(Subcommand::BuildSpec(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) - }, - Some(Subcommand::CheckBlock(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { client, task_manager, import_queue, .. } = - service::new_partial(&config)?; - Ok((cmd.run(client, import_queue), task_manager)) - }) - }, - Some(Subcommand::ExportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?; - Ok((cmd.run(client, config.database), task_manager)) - }) - }, - Some(Subcommand::ExportState(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?; - Ok((cmd.run(client, config.chain_spec), task_manager)) - }) - }, - Some(Subcommand::ImportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { client, task_manager, import_queue, .. } = - service::new_partial(&config)?; - Ok((cmd.run(client, import_queue), task_manager)) - }) - }, - Some(Subcommand::PurgeChain(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run(config.database)) - }, - Some(Subcommand::Revert(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { client, task_manager, backend, .. } = - service::new_partial(&config)?; - let aux_revert = Box::new(|client, _, blocks| { - sc_consensus_grandpa::revert(client, blocks)?; - Ok(()) - }); - Ok((cmd.run(client, backend, Some(aux_revert)), task_manager)) - }) - }, - Some(Subcommand::Benchmark(cmd)) => { - let runner = cli.create_runner(cmd)?; - - runner.sync_run(|config| { - // This switch needs to be in the client, since the client decides - // which sub-commands it wants to support. - match cmd { - BenchmarkCmd::Pallet(cmd) => { - if !cfg!(feature = "runtime-benchmarks") { - return Err( - "Runtime benchmarking wasn't enabled when building the node. \ - You can enable it with `--features runtime-benchmarks`." - .into(), - ); - } - - cmd.run::(config) - }, - BenchmarkCmd::Block(cmd) => { - let PartialComponents { client, .. } = service::new_partial(&config)?; - cmd.run(client) - }, - #[cfg(not(feature = "runtime-benchmarks"))] - BenchmarkCmd::Storage(_) => Err( - "Storage benchmarking can be enabled with `--features runtime-benchmarks`." - .into(), - ), - #[cfg(feature = "runtime-benchmarks")] - BenchmarkCmd::Storage(cmd) => { - let PartialComponents { client, backend, .. } = - service::new_partial(&config)?; - let db = backend.expose_db(); - let storage = backend.expose_storage(); - - cmd.run(config, client, db, storage) - }, - BenchmarkCmd::Overhead(cmd) => { - let PartialComponents { client, .. } = service::new_partial(&config)?; - let ext_builder = RemarkBuilder::new(client.clone()); - - cmd.run( - config, - client, - inherent_benchmark_data()?, - Vec::new(), - &ext_builder, - ) - }, - BenchmarkCmd::Extrinsic(cmd) => { - let PartialComponents { client, .. } = service::new_partial(&config)?; - // Register the *Remark* and *TKA* builders. - let ext_factory = ExtrinsicFactory(vec![ - Box::new(RemarkBuilder::new(client.clone())), - Box::new(TransferKeepAliveBuilder::new( - client.clone(), - Sr25519Keyring::Alice.to_account_id(), - EXISTENTIAL_DEPOSIT, - )), - ]); - - cmd.run(client, inherent_benchmark_data()?, Vec::new(), &ext_factory) - }, - BenchmarkCmd::Machine(cmd) => { - cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()) - }, +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "Tanssi Collator".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Tanssi Collator\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/cumulus/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2020 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + match id { + "westend_moonbase_relay_testnet" => Ok(Box::new(WestendChainSpec::from_json_bytes( + &include_bytes!("../../specs/dancebox/alphanet-relay-raw-specs.json")[..], + )?)), + // If we are not using a moonbeam-centric pre-baked relay spec, then fall back to the + // Polkadot service to interpret the id. + _ => polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()) + .load_spec(id), + } + } +} + +impl SubstrateCli for ContainerChainCli { + fn impl_name() -> String { + "Container chain".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Container chain\n\nThe command-line arguments provided first will be \ + passed to the orchestrator chain node, while the arguments provided after -- will be passed \ + to the container chain node, and the arguments provided after another -- will be passed \ + to the relay chain node\n\n\ + {} [orchestrator-args] -- [container-chain-args] -- [relay-chain-args] -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/cumulus/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2020 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + // ContainerChain ChainSpec must be preloaded beforehand because we need to call async + // functions to generate it, and this function is not async. + let para_id = parse_container_chain_id_str(id)?; + + match &self.preloaded_chain_spec { + Some(spec) => { + let spec_para_id = crate::chain_spec::Extensions::try_get(&**spec) + .map(|extension| extension.para_id); + + if spec_para_id == Some(para_id) { + Ok(spec.cloned_box()) + } else { + Err(format!( + "Expected ChainSpec for id {}, found ChainSpec for id {:?} instead", + para_id, spec_para_id + )) + } + } + None => Err(format!("ChainSpec for {} not found", id)), + } + } +} + +/// Parse ParaId(2000) from a string like "container-chain-2000" +fn parse_container_chain_id_str(id: &str) -> std::result::Result { + // The id has been created using format!("container-chain-{}", para_id), so here we need + // to reverse that. + id.strip_prefix("container-chain-") + .and_then(|s| { + let id: u32 = s.parse().ok()?; + + // `.parse()` ignores leading zeros, so convert the id back to string to check + // if we get the same string, this way we ensure a 1:1 mapping + if id.to_string() == s { + Some(id) + } else { + None + } + }) + .ok_or_else(|| format!("load_spec called with invalid id: {:?}", id)) +} + +macro_rules! construct_async_run { + (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{ + let runner = $cli.create_runner($cmd)?; + runner.async_run(|$config| { + let $components = NodeConfig::new_builder(&$config, None)?; + let inner = { $( $code )* }; + + let task_manager = $components.task_manager; + inner.map(|v| (v, task_manager)) + }) + }} +} + +/// Parse command line arguments into service configuration. +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let chain_spec = load_spec( + &cmd.base.chain_id(cmd.base.is_dev()?)?, + cmd.parachain_id, + cmd.add_container_chain.clone().unwrap_or_default(), + cmd.mock_container_chain.clone().unwrap_or_default(), + cmd.invulnerable.clone(), + )?; + cmd.base.run(chain_spec, config.network) + }) + } + Some(Subcommand::CheckBlock(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + let (_, import_queue) = service::import_queue(&config, &components); + Ok(cmd.run(components.client, import_queue)) + }) + } + Some(Subcommand::ExportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, config.database)) + }) + } + Some(Subcommand::ExportState(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, config.chain_spec)) + }) + } + Some(Subcommand::ImportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + let (_, import_queue) = service::import_queue(&config, &components); + Ok(cmd.run(components.client, import_queue)) + }) + } + Some(Subcommand::Revert(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.backend, None)) + }) + } + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| { + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()] + .iter() + .chain(cli.relaychain_args().iter()), + ); + + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + cmd.run(config, polkadot_config) + }) + } + Some(Subcommand::ExportGenesisHead(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let client = NodeConfig::new_builder(&config, None)?.client; + cmd.run(client) + }) + } + Some(Subcommand::ExportGenesisWasm(params)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); + let _ = builder.init(); + + let raw_wasm_blob = + extract_genesis_wasm(&*cli.load_spec(¶ms.chain.clone().unwrap_or_default())?)?; + let output_buf = if params.raw { + raw_wasm_blob + } else { + format!("0x{:?}", HexDisplay::from(&raw_wasm_blob)).into_bytes() + }; + + if let Some(output) = ¶ms.output { + std::fs::write(output, output_buf)?; + } else { + std::io::stdout().write_all(&output_buf)?; + } + + Ok(()) + } + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + // Switch on the concrete benchmark sub-command- + match cmd { + BenchmarkCmd::Pallet(cmd) => { + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + Err("Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + .into()) + } + } + BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { + let client = NodeConfig::new_builder(&config, None)?.client; + cmd.run(client) + }), + #[cfg(not(feature = "runtime-benchmarks"))] + BenchmarkCmd::Storage(_) => Err(sc_cli::Error::Input( + "Compile with --features=runtime-benchmarks \ + to enable storage benchmarks." + .into(), + )), + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { + let builder = NodeConfig::new_builder(&config, None)?; + let db = builder.backend.expose_db(); + let storage = builder.backend.expose_storage(); + cmd.run(config, builder.client, db, storage) + }), + BenchmarkCmd::Machine(cmd) => { + runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())) + } + // NOTE: this allows the Client to leniently implement + // new benchmark commands without requiring a companion MR. + #[allow(unreachable_patterns)] + _ => Err("Benchmarking sub-command unsupported".into()), + } + } + Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?), + #[cfg(feature = "try-runtime")] + Some(Subcommand::TryRuntime(_)) => { + Err("Substrate's `try-runtime` subcommand has been migrated \ + to a standalone CLI (https://github.com/paritytech/try-runtime-cli)" + .into()) + } + #[cfg(not(feature = "try-runtime"))] + Some(Subcommand::TryRuntime) => { + Err("Substrate's `try-runtime` subcommand has been migrated \ + to a standalone CLI (https://github.com/paritytech/try-runtime-cli)" + .into()) + } + Some(Subcommand::PrecompileWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let partials = NodeConfig::new_builder(&config, None)?; + Ok(( + cmd.run(partials.backend, config.chain_spec), + partials.task_manager, + )) + }) + } + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + let hwbench = (!cli.no_hardware_benchmarks).then_some( + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench(Some(database_path)) + })).flatten(); + + let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) + .map(|e| e.para_id) + .ok_or("Could not find parachain ID in chain-spec.")?; + + let id = ParaId::from(para_id); + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args().iter()), + ); + + let extension = chain_spec::Extensions::try_get(&*config.chain_spec); + + let relay_chain_id = extension.map(|e| e.relay_chain.clone()); + + let dev_service = + config.chain_spec.is_dev() || relay_chain_id == Some("dev-service".to_string()) || cli.run.dev_service; + + if dev_service { + return crate::service::start_dev_node(config, cli.run.sealing, hwbench, id).map_err(Into::into) } + + let parachain_account = + AccountIdConversion::::into_account_truncating(&id); + + let block: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V1) + .map_err(|e| format!("{:?}", e))?; + let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode())); + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + info!("Parachain id: {:?}", id); + info!("Parachain Account: {}", parachain_account); + info!("Parachain genesis state: {}", genesis_state); + info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + if let cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) = + collator_options.clone().relay_chain_mode { + if !rpc_target_urls.is_empty() && !cli.relaychain_args().is_empty() { + warn!("Detected relay chain node arguments together with --relay-chain-rpc-url. This command starts a minimal Polkadot node that only uses a network-related subset of all relay chain CLI options."); + } + } + + let mut container_chain_config = None; + // Even if container-chain-args are empty, we need to spawn the container-detection + // collation taks if the role is authority. + + // We need to bake in some container-chain args + if !cli.container_chain_args().is_empty() || config.role.is_authority() { + let container_chain_cli = ContainerChainCli::new( + &config, + [ContainerChainCli::executable_name()].iter().chain(cli.container_chain_args().iter()), + ); + let tokio_handle = config.tokio_handle.clone(); + container_chain_config = Some((container_chain_cli, tokio_handle)); + } + + crate::service::start_parachain_node( + config, + polkadot_config, + container_chain_config, + collator_options, + id, + hwbench, + ) + .await + .map(|r| r.0) + .map_err(Into::into) }) - }, - #[cfg(feature = "try-runtime")] - Some(Subcommand::TryRuntime(cmd)) => { - use crate::service::ExecutorDispatch; - use sc_executor::{sp_wasm_interface::ExtendedHostFunctions, NativeExecutionDispatch}; - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - // we don't need any of the components of new_partial, just a runtime, or a task - // manager to do `async_run`. - let registry = config.prometheus_config.as_ref().map(|cfg| &cfg.registry); - let task_manager = - sc_service::TaskManager::new(config.tokio_handle.clone(), registry) - .map_err(|e| sc_cli::Error::Service(sc_service::Error::Prometheus(e)))?; - let info_provider = timestamp_with_aura_info(6000); - - Ok(( - cmd.run::::ExtendHostFunctions, - >, _>(Some(info_provider)), - task_manager, - )) - }) - }, - #[cfg(not(feature = "try-runtime"))] - Some(Subcommand::TryRuntime) => Err("TryRuntime wasn't enabled when building the node. \ - You can enable it with `--features try-runtime`." - .into()), - Some(Subcommand::ChainInfo(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run::(&config)) - }, - None => { - let runner = cli.create_runner(&cli.run)?; - runner.run_node_until_exit(|config| async move { - service::new_full(config).map_err(sc_cli::Error::Service) - }) - }, - } + } + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_listen_port() -> u16 { + 9945 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| Some(self.base_path.clone().into()))) + } + + fn rpc_addr(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_addr(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base + .base + .prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { + self.chain_id.clone().unwrap_or_default() + } else { + chain_id + }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_max_connections(&self) -> Result { + self.base.base.rpc_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } +} + +impl DefaultConfigurationValues for ContainerChainCli { + fn p2p_listen_port() -> u16 { + 30335 + } + + fn rpc_listen_port() -> u16 { + 9946 + } + + fn prometheus_listen_port() -> u16 { + 9617 + } +} + +impl CliConfiguration for ContainerChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| Some(self.base_path.clone().into()))) + } + + fn rpc_addr(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_addr(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base + .base + .prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, _is_dev: bool) -> Result { + self.base + .para_id + .map(|para_id| format!("container-chain-{}", para_id)) + .ok_or("no para-id in container chain args".into()) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_max_connections(&self) -> Result { + self.base.base.rpc_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } } diff --git a/node/src/container_chain_monitor.rs b/node/src/container_chain_monitor.rs new file mode 100644 index 0000000..1fe8e9f --- /dev/null +++ b/node/src/container_chain_monitor.rs @@ -0,0 +1,326 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +use { + crate::{ + container_chain_spawner::{CcSpawnMsg, ContainerChainSpawnerState}, + service::{ContainerChainBackend, ContainerChainClient}, + }, + cumulus_primitives_core::ParaId, + std::{ + cell::Cell, + collections::VecDeque, + sync::{Arc, Mutex}, + time::Instant, + }, + tokio::{ + sync::mpsc::UnboundedSender, + time::{sleep, Duration}, + }, +}; + +#[derive(Default)] +pub struct SpawnedContainersMonitor { + /// List of the N most recently started container chains, with some statistics related to + /// stopping time and reference count. + list: VecDeque, + /// Count the number of times a container chain has been started + count: usize, +} + +pub struct SpawnedContainer { + /// Unique identifier for a spawned container (not ParaId) + pub id: usize, + /// Container chain para id + pub para_id: ParaId, + /// When did the container chain start + pub start_time: Instant, + /// When the container chain was asked to stop (`StopContainerChain` was dropped) + pub stop_signal_time: Option, + /// When the container chain task manager was dropped, this should finish all the background + /// tasks except the ones started in separate threads. + pub stop_task_manager_time: Option, + /// When the `monitor_task` first observed that the reference counts are all 0. + /// This won't be precise because it is checked using polling with a high period. + pub stop_refcount_time: Cell>, + /// Used to check the reference count, if it's 0 it means the database has been closed + pub backend: std::sync::Weak, + /// Used to check the reference count, if it's 0 it means that the client has been closed. + pub client: std::sync::Weak, +} + +impl SpawnedContainer { + pub fn is_stopped(&self) -> bool { + self.stop_refcount_time.get().is_some() || { + // Check reference count, and set stop_refcount_time if zero + let refcount_zero = self.backend.strong_count() == 0 && self.client.strong_count() == 0; + if refcount_zero { + self.stop_refcount_time.set(Some(Instant::now())); + + true + } else { + false + } + } + } + + pub fn summary(&self) -> String { + #[derive(Debug)] + #[allow(unused)] + struct SpawnedContainerSummary { + id: usize, + para_id: ParaId, + time_start_to_now: Duration, + time_start_to_stop_signal: Option, + time_stop_signal_to_stop_task_manager: Option, + time_stop_task_manager_to_stop_refcount: Option, + time_stop_refcount_to_now: Option, + backend_refcount: usize, + client_refcount: usize, + } + + let summary = SpawnedContainerSummary { + id: self.id, + para_id: self.para_id, + time_start_to_now: Instant::now().duration_since(self.start_time), + time_start_to_stop_signal: self + .stop_signal_time + .map(|x| x.duration_since(self.start_time)), + time_stop_signal_to_stop_task_manager: self + .stop_task_manager_time + .and_then(|x| Some(x.duration_since(self.stop_signal_time?))), + time_stop_task_manager_to_stop_refcount: self + .stop_refcount_time + .get() + .and_then(|x| Some(x.duration_since(self.stop_task_manager_time?))), + time_stop_refcount_to_now: self + .stop_refcount_time + .get() + .map(|x| Instant::now().duration_since(x)), + backend_refcount: self.backend.strong_count(), + client_refcount: self.client.strong_count(), + }; + + format!("{:?}", summary) + } +} + +impl SpawnedContainersMonitor { + /// Returns a unique id which is not the ParaId + pub fn push(&mut self, mut x: SpawnedContainer) -> usize { + assert_eq!(x.id, 0, "SpawnedContainer.id must be set to 0, the actual id will be returned from push function"); + let id = self.count; + x.id = id; + self.list.push_back(x); + self.count += 1; + + id + } + + pub fn set_stop_signal_time(&mut self, id: usize, when: Instant) { + let i = self.list.iter().position(|x| x.id == id); + + if let Some(i) = i { + self.list[i].stop_signal_time = Some(when); + } + } + + pub fn set_stop_task_manager_time(&mut self, id: usize, when: Instant) { + let i = self.list.iter().position(|x| x.id == id); + + if let Some(i) = i { + self.list[i].stop_task_manager_time = Some(when); + } + } + + #[allow(unused)] + pub fn set_stop_refcount_time(&mut self, id: usize, when: Instant) { + let i = self.list.iter().position(|x| x.id == id); + + if let Some(i) = i { + self.list[i].stop_refcount_time.set(Some(when)); + } + } + + pub fn running_chains(&self) -> Vec<&SpawnedContainer> { + self.list + .iter() + .filter(|container| !container.is_stopped()) + .collect() + } + + #[allow(unused)] + pub fn truncate_old(&mut self, new_len: usize) { + if self.list.len() <= new_len { + return; + } + + let idx_new_first_element = self.list.len() - new_len; + self.list.drain(0..idx_new_first_element); + } + + pub fn truncate_old_stopped_chains(&mut self, new_len: usize) -> Result<(), ()> { + if self.list.len() <= new_len { + return Ok(()); + } + + let mut to_retain = self.list.len() - new_len; + self.list.retain(|container| { + if to_retain == 0 { + return true; + } + + if container.is_stopped() { + to_retain -= 1; + false + } else { + true + } + }); + + if self.list.len() <= new_len { + Ok(()) + } else { + Err(()) + } + } +} + +/// Background task that monitors the number of running container chains. +pub async fn monitor_task(state: Arc>) { + // Main loop frequency, doesn't need to be fast + let monitor_period = Duration::from_secs(300 * 0 + 10); + // Max number of allowed container chains before printing warnings. + // There should be at most 2 container chains running at the same time (1 syncing + 1 collating), + // but add a margin of error because a container chain may take a few seconds to stop. + let max_running_container_chains = 4; + + loop { + sleep(monitor_period).await; + log::debug!("Monitor tick"); + let mut state = state.lock().unwrap(); + let monitor_state = &mut state.spawned_containers_monitor; + + let running_chains = monitor_state.running_chains(); + let running_para_ids: Vec = running_chains.iter().map(|x| x.para_id).collect(); + if running_chains.len() > max_running_container_chains { + log::warn!("Too many container chains running at the same time"); + log::warn!( + "Running container chains: {}: {:?}", + running_chains.len(), + running_para_ids + ); + log::debug!( + "{:?}", + running_chains + .iter() + .map(|x| x.summary()) + .collect::>() + ) + } else { + log::debug!( + "Running container chains: {}: {:?}", + running_chains.len(), + running_para_ids + ); + } + + // Remove stopped container chains to keep the list small + let _ = monitor_state.truncate_old_stopped_chains(10); + } +} + +#[allow(unused)] +/// Start and stop the same container chain in a loop, used for testing and debugging +pub async fn debug_start_and_stop_same_cc(cc_spawn_tx: UnboundedSender) { + let sleep_delay = Duration::from_secs(10); + + loop { + sleep(sleep_delay).await; + cc_spawn_tx + .send(CcSpawnMsg::UpdateAssignment { + current: Some(2000u32.into()), + next: None, + }) + .unwrap(); + sleep(sleep_delay).await; + cc_spawn_tx + .send(CcSpawnMsg::UpdateAssignment { + current: None, + next: None, + }) + .unwrap(); + sleep(sleep_delay).await; + cc_spawn_tx + .send(CcSpawnMsg::UpdateAssignment { + current: None, + next: Some(2001u32.into()), + }) + .unwrap(); + sleep(sleep_delay).await; + cc_spawn_tx + .send(CcSpawnMsg::UpdateAssignment { + current: None, + next: None, + }) + .unwrap(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_truncate() { + let mut monitor = SpawnedContainersMonitor::default(); + let default_container = || SpawnedContainer { + id: Default::default(), + para_id: Default::default(), + start_time: Instant::now(), + stop_signal_time: Default::default(), + stop_task_manager_time: Default::default(), + stop_refcount_time: Default::default(), + backend: Default::default(), + client: Default::default(), + }; + + // Truncating empty list does not panic + monitor.truncate_old(0); + monitor.truncate_old_stopped_chains(0).unwrap(); + + for _ in 0..20 { + monitor.push(default_container()); + } + + assert_eq!(monitor.list.len(), 20); + assert_eq!(monitor.count, 20); + + monitor.truncate_old(15); + assert_eq!(monitor.list.len(), 15); + assert_eq!(monitor.count, 20); + // Truncate should remove the oldest stopped chains, so the first id is now 5 + assert_eq!(monitor.list.front().map(|x| x.id), Some(5)); + + // We are using Default::default which has a refcount of 0, so all chains are considered stopped + assert!(monitor.list.iter().all(|x| x.is_stopped())); + monitor.truncate_old_stopped_chains(10).unwrap(); + assert_eq!(monitor.list.len(), 10); + assert_eq!(monitor.count, 20); + // Truncate should remove the oldest stopped chains, so the first id is now 10 + assert_eq!(monitor.list.front().map(|x| x.id), Some(10)); + } +} diff --git a/node/src/container_chain_spawner.rs b/node/src/container_chain_spawner.rs new file mode 100644 index 0000000..8670ae9 --- /dev/null +++ b/node/src/container_chain_spawner.rs @@ -0,0 +1,1220 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! Container Chain Spawner +//! +//! Controls the starting and stopping of container chains. +//! +//! For more information about when the database is deleted, check the +//! [Keep db flowchart](https://raw.githubusercontent.com/moondance-labs/tanssi/master/docs/keep_db_flowchart.png) + +use { + crate::{ + cli::ContainerChainCli, + container_chain_monitor::{SpawnedContainer, SpawnedContainersMonitor}, + service::{start_node_impl_container, NodeConfig, ParachainClient}, + }, + cumulus_primitives_core::ParaId, + cumulus_relay_chain_interface::RelayChainInterface, + dancebox_runtime::{AccountId, Block, BlockNumber}, + dc_orchestrator_chain_interface::OrchestratorChainInterface, + futures::FutureExt, + node_common::{command::generate_genesis_block, service::NodeBuilderConfig}, + pallet_author_noting_runtime_api::AuthorNotingApi, + pallet_registrar_runtime_api::RegistrarApi, + polkadot_primitives::CollatorPair, + sc_cli::{Database, SyncMode}, + sc_network::config::MultiaddrWithPeerId, + sc_service::SpawnTaskHandle, + sp_api::{ApiExt, ProvideRuntimeApi}, + sp_keystore::KeystorePtr, + sp_runtime::traits::Block as BlockT, + std::{ + collections::{HashMap, HashSet}, + future::Future, + path::Path, + pin::Pin, + sync::{Arc, Mutex}, + time::Instant, + }, + tokio::{ + sync::{mpsc, oneshot}, + time::{sleep, Duration}, + }, + tokio_util::sync::CancellationToken, +}; + +/// Struct with all the params needed to start a container chain node given the CLI arguments, +/// and creating the ChainSpec from on-chain data from the orchestrator chain. + +pub struct ContainerChainSpawner { + // Start container chain params + pub orchestrator_chain_interface: Arc, + pub orchestrator_client: Arc, + pub container_chain_cli: ContainerChainCli, + pub tokio_handle: tokio::runtime::Handle, + pub chain_type: sc_chain_spec::ChainType, + pub relay_chain: String, + pub relay_chain_interface: Arc, + pub collator_key: Option, + pub sync_keystore: KeystorePtr, + pub orchestrator_para_id: ParaId, + pub validator: bool, + pub spawn_handle: SpawnTaskHandle, + + // State + pub state: Arc>, + + // Async callback that enables collation on the orchestrator chain + pub collate_on_tanssi: + Arc (CancellationToken, futures::channel::oneshot::Receiver<()>) + Send + Sync>, + // Stores the cancellation token used to stop the orchestrator chain collator process. + // When this is None, the orchestrator collator is not running. + pub collation_cancellation_constructs: + Option<(CancellationToken, futures::channel::oneshot::Receiver<()>)>, +} + +#[derive(Default)] +pub struct ContainerChainSpawnerState { + spawned_container_chains: HashMap, + assigned_para_id: Option, + next_assigned_para_id: Option, + failed_para_ids: HashSet, + // For debugging and detecting errors + pub spawned_containers_monitor: SpawnedContainersMonitor, +} + +pub struct ContainerChainState { + /// Handle that can be used to stop the container chain + stop_handle: StopContainerChain, +} + +/// Stops a container chain when signal is sent. The bool means `keep_db`, whether to keep the +/// container chain database (true) or remove it (false). +pub struct StopContainerChain { + signal: oneshot::Sender, + id: usize, +} + +/// Messages used to control the `ContainerChainSpawner`. This is needed because one of the fields +/// of `ContainerChainSpawner` is not `Sync`, so we cannot simply pass an +/// `Arc` to other threads. +#[derive(Debug)] +pub enum CcSpawnMsg { + /// Update container chain assignment + UpdateAssignment { + current: Option, + next: Option, + }, +} + +impl ContainerChainSpawner { + /// Try to start a new container chain. In case of an error, this does not stop the node, and + /// the container chain will be attempted to spawn again when the collator is reassigned to it. + #[must_use] + fn spawn( + &self, + container_chain_para_id: ParaId, + start_collation: bool, + ) -> Pin + Send>> { + let ( + orchestrator_chain_interface, + orchestrator_client, + mut container_chain_cli, + tokio_handle, + chain_type, + relay_chain, + relay_chain_interface, + collator_key, + sync_keystore, + orchestrator_para_id, + validator, + spawn_handle, + state, + ) = ( + self.orchestrator_chain_interface.clone(), + self.orchestrator_client.clone(), + self.container_chain_cli.clone(), + self.tokio_handle.clone(), + self.chain_type.clone(), + self.relay_chain.clone(), + self.relay_chain_interface.clone(), + self.collator_key.clone(), + self.sync_keystore.clone(), + self.orchestrator_para_id, + self.validator, + self.spawn_handle.clone(), + self.state.clone(), + ); + let state2 = state.clone(); + // This closure is used to emulate a try block, it enables using the `?` operator inside + let try_closure = move || async move { + // Preload genesis data from orchestrator chain storage. + // The preload must finish before calling create_configuration, so any async operations + // need to be awaited. + + // TODO: the orchestrator chain node may not be fully synced yet, + // in that case we will be reading an old state. + let orchestrator_chain_info = orchestrator_client.chain_info(); + log::info!( + "Reading container chain genesis data from orchestrator chain at block #{} {}", + orchestrator_chain_info.best_number, + orchestrator_chain_info.best_hash, + ); + let orchestrator_runtime_api = orchestrator_client.runtime_api(); + + log::info!( + "Detected assignment for container chain {}", + container_chain_para_id + ); + + let genesis_data = orchestrator_runtime_api + .genesis_data(orchestrator_chain_info.best_hash, container_chain_para_id) + .map_err(|e| format!("Failed to call genesis_data runtime api: {}", e))? + .ok_or_else(|| { + format!( + "No genesis data registered for container chain id {}", + container_chain_para_id + ) + })?; + + let boot_nodes_raw = orchestrator_runtime_api + .boot_nodes(orchestrator_chain_info.best_hash, container_chain_para_id) + .map_err(|e| format!("Failed to call boot_nodes runtime api: {}", e))?; + if boot_nodes_raw.is_empty() { + log::warn!( + "No boot nodes registered on-chain for container chain {}", + container_chain_para_id + ); + } + let boot_nodes = + parse_boot_nodes_ignore_invalid(boot_nodes_raw, container_chain_para_id); + if boot_nodes.is_empty() { + log::warn!( + "No valid boot nodes for container chain {}", + container_chain_para_id + ); + } + + container_chain_cli + .preload_chain_spec_from_genesis_data( + container_chain_para_id.into(), + genesis_data, + chain_type.clone(), + relay_chain.clone(), + boot_nodes, + ) + .map_err(|e| format!("failed to create container chain chain spec from on chain genesis data: {}", e))?; + + log::info!( + "Loaded chain spec for container chain {}", + container_chain_para_id + ); + + if !start_collation { + log::info!("This is a syncing container chain, using random ports"); + // Use random ports to avoid conflicts with the other running container chain + let random_ports = [23456, 23457, 23458]; + container_chain_cli + .base + .base + .prometheus_params + .prometheus_port = Some(random_ports[0]); + container_chain_cli.base.base.network_params.port = Some(random_ports[1]); + container_chain_cli.base.base.rpc_port = Some(random_ports[2]); + } + + // Update CLI params + container_chain_cli.base.para_id = Some(container_chain_para_id.into()); + container_chain_cli + .base + .base + .import_params + .database_params + .database = Some(Database::ParityDb); + + let create_container_chain_cli_config = || { + let mut container_chain_cli_config = sc_cli::SubstrateCli::create_configuration( + &container_chain_cli, + &container_chain_cli, + tokio_handle.clone(), + ) + .map_err(|err| format!("Container chain argument error: {}", err))?; + + // Change database path to make it depend on container chain para id + // So instead of the usual "db/full" we have "db/full-container-2000" + let mut db_path = container_chain_cli_config + .database + .path() + .ok_or_else(|| "Failed to get database path".to_string())? + .to_owned(); + db_path.set_file_name(format!("full-container-{}", container_chain_para_id)); + container_chain_cli_config.database.set_path(&db_path); + + sc_service::error::Result::Ok((container_chain_cli_config, db_path)) + }; + + let (_container_chain_cli_config, db_path) = create_container_chain_cli_config()?; + let db_exists = db_path.exists(); + let db_exists_but_may_need_removal = db_exists && validator; + if db_exists_but_may_need_removal { + // If the database exists it may be invalid (genesis hash mismatch), so check if it is valid + // and if not, delete it. + // Create a new cli config because otherwise the tasks spawned in `open_and_maybe_delete_db` don't stop + let (container_chain_cli_config, db_path) = create_container_chain_cli_config()?; + open_and_maybe_delete_db( + container_chain_cli_config, + &db_path, + &orchestrator_client, + container_chain_para_id, + &container_chain_cli, + container_chain_cli.base.keep_db, + )?; + // Need to add a sleep here to ensure that the partial components created in + // `open_and_maybe_delete_db` have enough time to close. + log::info!("Restarting container chain {}", container_chain_para_id); + sleep(Duration::from_secs(10)).await; + } + + // Select appropiate sync mode. We want to use WarpSync unless the db still exists, + // or the block number is 0 (because of a warp sync bug in that case). + let db_still_exists = db_path.exists(); + container_chain_cli.base.base.network_params.sync = select_sync_mode( + db_still_exists, + &orchestrator_client, + container_chain_para_id, + )?; + log::info!( + "Container chain sync mode: {:?}", + container_chain_cli.base.base.network_params.sync + ); + let mut container_chain_cli_config = sc_cli::SubstrateCli::create_configuration( + &container_chain_cli, + &container_chain_cli, + tokio_handle.clone(), + ) + .map_err(|err| format!("Container chain argument error: {}", err))?; + container_chain_cli_config.database.set_path(&db_path); + + // Start container chain node + let (mut container_chain_task_manager, container_chain_client, container_chain_db) = + start_node_impl_container( + container_chain_cli_config, + orchestrator_client.clone(), + relay_chain_interface.clone(), + orchestrator_chain_interface.clone(), + collator_key.clone(), + sync_keystore.clone(), + container_chain_para_id, + orchestrator_para_id, + validator && start_collation, + ) + .await?; + + // Signal that allows to gracefully stop a container chain + let (signal, on_exit) = oneshot::channel::(); + + let monitor_id; + { + let mut state = state.lock().expect("poison error"); + + monitor_id = state.spawned_containers_monitor.push(SpawnedContainer { + id: 0, + para_id: container_chain_para_id, + start_time: Instant::now(), + stop_signal_time: None, + stop_task_manager_time: None, + stop_refcount_time: Default::default(), + backend: Arc::downgrade(&container_chain_db), + client: Arc::downgrade(&container_chain_client), + }); + + state.spawned_container_chains.insert( + container_chain_para_id, + ContainerChainState { + stop_handle: StopContainerChain { + signal, + id: monitor_id, + }, + }, + ); + } + + // Add the container chain task manager as a child task to the parent task manager. + // We want to stop the node if this task manager stops, but we also want to allow a + // graceful shutdown using the `on_exit` future. + let name = "container-chain-task-manager"; + spawn_handle.spawn(name, None, async move { + let mut container_chain_task_manager_future = + container_chain_task_manager.future().fuse(); + let mut on_exit_future = on_exit.fuse(); + + futures::select! { + res1 = container_chain_task_manager_future => { + // An essential task failed or the task manager was stopped unexpectedly + // using `.terminate()`. This should stop the container chain but not the node. + if res1.is_err() { + log::error!("Essential task failed in container chain {} task manager. Shutting down container chain service", container_chain_para_id); + } else { + log::error!("Unexpected shutdown in container chain {} task manager. Shutting down container chain service", container_chain_para_id); + } + // Mark this container chain as "failed to stop" to avoid warning in `self.stop()` + let mut state = state.lock().expect("poison error"); + state.failed_para_ids.insert(container_chain_para_id); + // Never delete db in this case because it is not a graceful shutdown + } + stop_unassigned = on_exit_future => { + // Graceful shutdown. + // `stop_unassigned` will be `Ok(keep_db)` if `.stop()` has been called, which means that the + // container chain has been unassigned, and will be `Err` if the handle has been dropped, + // which means that the node is stopping. + // Delete existing database if running as collator + if validator && stop_unassigned == Ok(false) && !container_chain_cli.base.keep_db { + delete_container_chain_db(&db_path); + } + } + } + + let mut state = state.lock().expect("poison error"); + state + .spawned_containers_monitor + .set_stop_task_manager_time(monitor_id, Instant::now()); + }); + + sc_service::error::Result::Ok(()) + }; + + async move { + match try_closure().await { + Ok(()) => {} + Err(e) => { + log::error!( + "Failed to start container chain {}: {}", + container_chain_para_id, + e + ); + // Mark this container chain as "failed to start" + let mut state = state2.lock().expect("poison error"); + state.failed_para_ids.insert(container_chain_para_id); + } + } + } + .boxed() + } + + /// Stop a container chain. Prints a warning if the container chain was not running. + fn stop(&self, container_chain_para_id: ParaId, keep_db: bool) { + let mut state = self.state.lock().expect("poison error"); + let stop_handle = state + .spawned_container_chains + .remove(&container_chain_para_id); + + match stop_handle { + Some(stop_handle) => { + log::info!("Stopping container chain {}", container_chain_para_id); + + let id = stop_handle.stop_handle.id; + state + .spawned_containers_monitor + .set_stop_signal_time(id, Instant::now()); + + // Send signal to perform graceful shutdown, which will delete the db if needed + let _ = stop_handle.stop_handle.signal.send(keep_db); + } + None => { + // Do not print the warning message if this is a container chain that has failed to + // start, because in that case it will not be running + if !state.failed_para_ids.remove(&container_chain_para_id) { + log::warn!( + "Tried to stop a container chain that is not running: {}", + container_chain_para_id + ); + } + } + } + } + + /// Receive and process `CcSpawnMsg`s indefinitely + pub async fn rx_loop(mut self, mut rx: mpsc::UnboundedReceiver, validator: bool) { + // The node always starts as an orchestrator chain collator. + // This is because the assignment is detected after importing a new block, so if all + // collators stop at the same time, when they start again nobody will produce the new block. + // So all nodes start as orchestrator chain collators, until the first block is imported, + // then the real assignment is used. + if validator { + self.handle_update_assignment(Some(self.orchestrator_para_id), None) + .await; + } + + while let Some(msg) = rx.recv().await { + match msg { + CcSpawnMsg::UpdateAssignment { current, next } => { + self.handle_update_assignment(current, next).await; + } + } + } + + // The while loop can end if all the senders get dropped, but since this is an + // essential task we don't want it to stop. So await a future that never completes. + // This should only happen when starting a full node. + if !validator { + let () = std::future::pending().await; + } + } + + /// Handle `CcSpawnMsg::UpdateAssignment` + async fn handle_update_assignment(&mut self, current: Option, next: Option) { + let HandleUpdateAssignmentResult { + chains_to_stop, + chains_to_start, + need_to_restart, + } = handle_update_assignment_state_change( + &mut self.state.lock().expect("poison error"), + self.orchestrator_para_id, + current, + next, + ); + + if current != Some(self.orchestrator_para_id) { + // If not assigned to orchestrator chain anymore, we need to stop the collator process + let maybe_exit_notification_receiver = self + .collation_cancellation_constructs + .take() + .map(|(cancellation_token, exit_notification_receiver)| { + cancellation_token.cancel(); + exit_notification_receiver + }); + + if let Some(exit_notification_receiver) = maybe_exit_notification_receiver { + let _ = exit_notification_receiver.await; + } + } else if self.collation_cancellation_constructs.is_none() { + // If assigned to orchestrator chain but the collator process is not running, start it + self.collation_cancellation_constructs = Some((self.collate_on_tanssi)()); + } + + // Stop all container chains that are no longer needed + for para_id in chains_to_stop { + // Keep db if we are currently assigned to this chain + let keep_db = Some(para_id) == current; + self.stop(para_id, keep_db); + } + + if need_to_restart { + // Give it some time to stop properly + sleep(Duration::from_secs(10)).await; + } + + // Start all new container chains (usually 1) + for para_id in chains_to_start { + // Edge case: when starting the node it may be assigned to a container chain, so we need to + // start a container chain already collating. + let start_collation = Some(para_id) == current; + self.spawn(para_id, start_collation).await; + } + } +} + +struct HandleUpdateAssignmentResult { + chains_to_stop: Vec, + chains_to_start: Vec, + need_to_restart: bool, +} + +// This is a separate function to allow testing +fn handle_update_assignment_state_change( + state: &mut ContainerChainSpawnerState, + orchestrator_para_id: ParaId, + current: Option, + next: Option, +) -> HandleUpdateAssignmentResult { + if (state.assigned_para_id, state.next_assigned_para_id) == (current, next) { + // If nothing changed there is nothing to update + return HandleUpdateAssignmentResult { + chains_to_stop: Default::default(), + chains_to_start: Default::default(), + need_to_restart: false, + }; + } + + // Create a set with the container chains that were running before, and the container + // chains that should be running after the updated assignment. This is used to calculate + // the difference, and stop and start the required container chains. + let mut running_chains_before = HashSet::new(); + let mut running_chains_after = HashSet::new(); + + running_chains_before.extend(state.assigned_para_id); + running_chains_before.extend(state.next_assigned_para_id); + // Ignore orchestrator_para_id because it is handled in a special way, as it does not need to + // start one session before in order to sync. + running_chains_before.remove(&orchestrator_para_id); + + running_chains_after.extend(current); + running_chains_after.extend(next); + running_chains_after.remove(&orchestrator_para_id); + let mut need_to_restart_current = false; + let mut need_to_restart_next = false; + + if state.assigned_para_id != current { + if let Some(para_id) = current { + // If the assigned container chain has changed, we may need to + // restart it in collation mode, unless it is the orchestrator chain. + if para_id != orchestrator_para_id { + need_to_restart_current = true; + } + } + + if let Some(para_id) = state.assigned_para_id { + if para_id != orchestrator_para_id && Some(para_id) == next { + need_to_restart_next = true; + } + } + } + + state.assigned_para_id = current; + state.next_assigned_para_id = next; + + let mut chains_to_stop: Vec<_> = running_chains_before + .difference(&running_chains_after) + .copied() + .collect(); + let mut chains_to_start: Vec<_> = running_chains_after + .difference(&running_chains_before) + .copied() + .collect(); + + if need_to_restart_current { + // Force restart of new assigned container chain: if it was running before it was in "syncing mode", + // which doesn't use the correct ports, so start it in "collation mode". + let id = current.unwrap(); + if running_chains_before.contains(&id) && !chains_to_stop.contains(&id) { + chains_to_stop.push(id); + } + if !chains_to_start.contains(&id) { + chains_to_start.push(id); + } + } + + if need_to_restart_next { + // Handle edge case of going from (2000, 2001) to (2001, 2000). In that case we must restart both chains, + // because previously 2000 was collating and now 2000 will only be syncing. + let id = next.unwrap(); + if running_chains_before.contains(&id) && !chains_to_stop.contains(&id) { + chains_to_stop.push(id); + } + if !chains_to_start.contains(&id) { + chains_to_start.push(id); + } + } + + HandleUpdateAssignmentResult { + chains_to_stop, + chains_to_start, + need_to_restart: need_to_restart_current || need_to_restart_next, + } +} + +/// Select `SyncMode` to use for a container chain. +/// We want to use warp sync unless the db still exists, or the block number is 0 (because of a warp sync bug in that case). +/// The reason is that warp sync doesn't work if a database already exists, it falls back to full sync instead. +fn select_sync_mode( + db_exists: bool, + orchestrator_client: &Arc, + container_chain_para_id: ParaId, +) -> sc_service::error::Result { + if db_exists { + // If the user wants to use warp sync, they should have already removed the database + return Ok(SyncMode::Full); + } + + // The following check is only needed because of this bug: + // https://github.com/paritytech/polkadot-sdk/issues/1930 + + let orchestrator_runtime_api = orchestrator_client.runtime_api(); + let orchestrator_chain_info = orchestrator_client.chain_info(); + + // Force container chains to use warp sync, unless full sync is needed for some reason + let full_sync_needed = if !orchestrator_runtime_api + .has_api::>( + orchestrator_chain_info.best_hash, + ) + .map_err(|e| format!("Failed to check if runtime has AuthorNotingApi: {}", e))? + { + // Before runtime API was implemented we don't know if the container chain has any blocks, + // so use full sync because that always works + true + } else { + // If the container chain is still at genesis block, use full sync because warp sync is broken + orchestrator_runtime_api + .latest_author(orchestrator_chain_info.best_hash, container_chain_para_id) + .map_err(|e| format!("Failed to read latest author: {}", e))? + .is_none() + }; + + if full_sync_needed { + Ok(SyncMode::Full) + } else { + Ok(SyncMode::Warp) + } +} + +/// Start a container chain using `new_partial` and check if the database is valid. If not, delete the db. +/// The caller may need to wait a few seconds before trying to start the same container chain again, to +/// give the database enough time to close. +// TODO: instead of waiting, we could also return Weak references to the components `temp_cli.backend` +// and `temp_cli.client`, and then the caller would only need to check if the reference counts are 0. +fn open_and_maybe_delete_db( + container_chain_cli_config: sc_service::Configuration, + db_path: &Path, + orchestrator_client: &Arc, + container_chain_para_id: ParaId, + container_chain_cli: &ContainerChainCli, + keep_db: bool, +) -> sc_service::error::Result<()> { + let temp_cli = NodeConfig::new_builder(&container_chain_cli_config, None)?; + + // Check block diff, only needed if keep-db is false + if !keep_db { + // Get latest block number from the container chain client + let last_container_block_temp = temp_cli.client.chain_info().best_number; + + let orchestrator_runtime_api = orchestrator_client.runtime_api(); + let orchestrator_chain_info = orchestrator_client.chain_info(); + // Get the container chain's latest block from orchestrator chain and compare with client's one + let last_container_block_from_orchestrator = orchestrator_runtime_api + .latest_block_number(orchestrator_chain_info.best_hash, container_chain_para_id) + .unwrap_or_default(); + + let max_block_diff_allowed = 100u32; + if last_container_block_from_orchestrator + .unwrap_or(0u32) + .abs_diff(last_container_block_temp) + > max_block_diff_allowed + { + // if the diff is big, delete db and restart using warp sync + delete_container_chain_db(db_path); + return Ok(()); + } + } + + // Generate genesis hash to compare against container client's genesis hash + let container_preloaded_genesis = container_chain_cli.preloaded_chain_spec.as_ref().unwrap(); + + // Check with both state versions + let block_v0: Block = + generate_genesis_block(&**container_preloaded_genesis, sp_runtime::StateVersion::V0) + .map_err(|e| format!("{:?}", e))?; + let chain_spec_genesis_hash_v0 = block_v0.header().hash(); + + let block_v1: Block = + generate_genesis_block(&**container_preloaded_genesis, sp_runtime::StateVersion::V1) + .map_err(|e| format!("{:?}", e))?; + let chain_spec_genesis_hash_v1 = block_v1.header().hash(); + + let container_client_genesis_hash = temp_cli.client.chain_info().genesis_hash; + + if container_client_genesis_hash != chain_spec_genesis_hash_v0 + && container_client_genesis_hash != chain_spec_genesis_hash_v1 + { + log::info!("Container genesis V0: {:?}", chain_spec_genesis_hash_v0); + log::info!("Container genesis V1: {:?}", chain_spec_genesis_hash_v1); + log::info!( + "Chain spec genesis {:?} did not match with any container genesis - Restarting...", + container_client_genesis_hash + ); + delete_container_chain_db(db_path); + return Ok(()); + } + + Ok(()) +} + +// TODO: this leaves some empty folders behind, because it is called with db_path: +// Collator2002-01/data/containers/chains/simple_container_2002/paritydb/full-container-2002 +// but we want to delete everything under +// Collator2002-01/data/containers/chains/simple_container_2002 +fn delete_container_chain_db(db_path: &Path) { + if db_path.exists() { + std::fs::remove_dir_all(db_path).expect("failed to remove old container chain db"); + } +} + +/// Parse a list of boot nodes in `Vec` format. Invalid boot nodes are filtered out. +fn parse_boot_nodes_ignore_invalid( + boot_nodes_raw: Vec>, + container_chain_para_id: ParaId, +) -> Vec { + boot_nodes_raw + .into_iter() + .filter_map(|x| { + let x = String::from_utf8(x) + .map_err(|e| { + log::debug!( + "Invalid boot node in container chain {}: {}", + container_chain_para_id, + e + ); + }) + .ok()?; + + x.parse::() + .map_err(|e| { + log::debug!( + "Invalid boot node in container chain {}: {}", + container_chain_para_id, + e + ) + }) + .ok() + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + // Copy of ContainerChainSpawner with extra assertions for tests, and mocked spawn function. + struct MockContainerChainSpawner { + state: Arc>, + orchestrator_para_id: ParaId, + collate_on_tanssi: Arc< + dyn Fn() -> (CancellationToken, futures::channel::oneshot::Receiver<()>) + Send + Sync, + >, + collation_cancellation_constructs: Option<()>, + // Keep track of the last CollateOn message, for tests + currently_collating_on: Arc>>, + } + + impl MockContainerChainSpawner { + fn new() -> Self { + let orchestrator_para_id = 1000.into(); + // The node always starts as an orchestrator chain collator + let currently_collating_on = Arc::new(Mutex::new(Some(orchestrator_para_id))); + let currently_collating_on2 = currently_collating_on.clone(); + let collate_closure = move || { + let mut cco = currently_collating_on2.lock().unwrap(); + assert_ne!( + *cco, + Some(orchestrator_para_id), + "Received CollateOn message when we were already collating on this chain: {}", + orchestrator_para_id + ); + *cco = Some(orchestrator_para_id); + let (_, receiver) = futures::channel::oneshot::channel(); + (CancellationToken::new(), receiver) + }; + let collate_on_tanssi: Arc< + dyn Fn() -> (CancellationToken, futures::channel::oneshot::Receiver<()>) + + Send + + Sync, + > = Arc::new(collate_closure); + + Self { + state: Arc::new(Mutex::new(ContainerChainSpawnerState { + spawned_container_chains: Default::default(), + assigned_para_id: Some(orchestrator_para_id), + next_assigned_para_id: None, + failed_para_ids: Default::default(), + spawned_containers_monitor: Default::default(), + })), + orchestrator_para_id, + collate_on_tanssi, + // Some if collator starts on orchestrator chain + collation_cancellation_constructs: Some(()), + currently_collating_on, + } + } + + fn spawn(&self, container_chain_para_id: ParaId, start_collation: bool) { + let (signal, _on_exit) = oneshot::channel(); + let currently_collating_on2 = self.currently_collating_on.clone(); + let collate_closure = move || { + let mut cco = currently_collating_on2.lock().unwrap(); + assert_ne!( + *cco, + Some(container_chain_para_id), + "Received CollateOn message when we were already collating on this chain: {}", + container_chain_para_id + ); + *cco = Some(container_chain_para_id); + let (_, receiver) = futures::channel::oneshot::channel(); + (CancellationToken::new(), receiver) + }; + let collate_on: Arc< + dyn Fn() -> (CancellationToken, futures::channel::oneshot::Receiver<()>) + + Send + + Sync, + > = Arc::new(collate_closure); + + let old = self + .state + .lock() + .expect("poison error") + .spawned_container_chains + .insert( + container_chain_para_id, + ContainerChainState { + stop_handle: StopContainerChain { signal, id: 0 }, + }, + ); + + assert!( + old.is_none(), + "tried to spawn a container chain that was already running: {}", + container_chain_para_id + ); + + if start_collation { + let (_cancellation_token, _exit_receiver) = collate_on(); + } + } + + fn stop(&self, container_chain_para_id: ParaId) { + let stop_handle = self + .state + .lock() + .expect("poison error") + .spawned_container_chains + .remove(&container_chain_para_id); + + match stop_handle { + Some(_stop_handle) => { + log::info!("Stopping container chain {}", container_chain_para_id); + } + None => { + panic!( + "Tried to stop a container chain that is not running: {}", + container_chain_para_id + ); + } + } + + // Update currently_collating_on, if we stopped the chain we are no longer collating there + let mut lco = self.currently_collating_on.lock().unwrap(); + if *lco == Some(container_chain_para_id) { + *lco = None; + } + } + + fn handle_update_assignment(&mut self, current: Option, next: Option) { + let HandleUpdateAssignmentResult { + chains_to_stop, + chains_to_start, + need_to_restart, + } = handle_update_assignment_state_change( + &mut self.state.lock().unwrap(), + self.orchestrator_para_id, + current, + next, + ); + + if current != Some(self.orchestrator_para_id) { + // If not assigned to orchestrator chain anymore, we need to stop the collator process + let mut cco = self.currently_collating_on.lock().unwrap(); + if *cco == Some(self.orchestrator_para_id) { + *cco = None; + } + self.collation_cancellation_constructs = None; + } else if self.collation_cancellation_constructs.is_none() { + let (_cancellation_token, _exit_notification_receiver) = (self.collate_on_tanssi)(); + self.collation_cancellation_constructs = Some(()); + } + + // Assert we never start and stop the same container chain + for para_id in &chains_to_start { + if !need_to_restart { + assert!( + !chains_to_stop.contains(para_id), + "Tried to start and stop same container chain: {}", + para_id + ); + } else { + // Will try to start and stop container chain with id "current" or "next", so ignore that + if Some(*para_id) != current && Some(*para_id) != next { + assert!( + !chains_to_stop.contains(para_id), + "Tried to start and stop same container chain: {}", + para_id + ); + } + } + } + // Assert we never start or stop the orchestrator chain + assert!(!chains_to_start.contains(&self.orchestrator_para_id)); + assert!(!chains_to_stop.contains(&self.orchestrator_para_id)); + + // Stop all container chains that are no longer needed + for para_id in chains_to_stop { + self.stop(para_id); + } + + // Start all new container chains (usually 1) + for para_id in chains_to_start { + // Edge case: when starting the node it may be assigned to a container chain, so we need to + // start a container chain already collating. + let start_collation = Some(para_id) == current; + self.spawn(para_id, start_collation); + } + + // Assert that if we are currently assigned to a container chain, we are collating there + if let Some(para_id) = current { + self.assert_collating_on(Some(para_id)); + } else { + self.assert_collating_on(None); + } + } + + #[track_caller] + fn assert_collating_on(&self, para_id: Option) { + let currently_collating_on = *self.currently_collating_on.lock().unwrap(); + assert_eq!(currently_collating_on, para_id); + } + + #[track_caller] + fn assert_running_chains(&self, para_ids: &[ParaId]) { + let mut actually_running: Vec = self + .state + .lock() + .unwrap() + .spawned_container_chains + .keys() + .cloned() + .collect(); + actually_running.sort(); + let mut should_be_running = para_ids.to_vec(); + should_be_running.sort(); + assert_eq!(actually_running, should_be_running); + } + } + + #[test] + fn starts_collating_on_tanssi() { + let mut m = MockContainerChainSpawner::new(); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + + m.handle_update_assignment(None, None); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + } + + #[test] + fn assigned_to_orchestrator_chain() { + let mut m = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(1000.into()), Some(1000.into())); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + + m.handle_update_assignment(Some(1000.into()), None); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + + m.handle_update_assignment(None, None); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + + m.handle_update_assignment(None, Some(1000.into())); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + + m.handle_update_assignment(Some(1000.into()), Some(1000.into())); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + } + + #[test] + fn assigned_to_container_chain() { + let mut m = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(2000.into()), Some(2000.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(Some(2000.into()), None); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(None, None); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + + m.handle_update_assignment(None, Some(2000.into())); + m.assert_collating_on(None); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(Some(2000.into()), Some(2000.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + } + + #[test] + fn spawn_container_chains() { + let mut m = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(1000.into()), Some(2000.into())); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(Some(2000.into()), Some(2000.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(Some(2000.into()), Some(2001.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into(), 2001.into()]); + + m.handle_update_assignment(Some(2001.into()), Some(2001.into())); + m.assert_collating_on(Some(2001.into())); + m.assert_running_chains(&[2001.into()]); + + m.handle_update_assignment(Some(2001.into()), Some(1000.into())); + m.assert_collating_on(Some(2001.into())); + m.assert_running_chains(&[2001.into()]); + + m.handle_update_assignment(Some(1000.into()), Some(1000.into())); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + } + + #[test] + fn swap_current_next() { + // Going from (2000, 2001) to (2001, 2000) shouldn't start or stop any container chains + let mut m: MockContainerChainSpawner = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(2000.into()), Some(2001.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into(), 2001.into()]); + + m.handle_update_assignment(Some(2001.into()), Some(2000.into())); + m.assert_collating_on(Some(2001.into())); + m.assert_running_chains(&[2000.into(), 2001.into()]); + } + + #[test] + fn stop_collating_orchestrator() { + let mut m: MockContainerChainSpawner = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(1000.into()), Some(1000.into())); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + + m.handle_update_assignment(Some(1000.into()), None); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + + m.handle_update_assignment(None, None); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + + m.handle_update_assignment(Some(1000.into()), None); + m.assert_collating_on(Some(1000.into())); + m.assert_running_chains(&[]); + } + + #[test] + fn stop_collating_container() { + let mut m: MockContainerChainSpawner = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(2000.into()), None); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(None, None); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + + m.handle_update_assignment(None, Some(2000.into())); + m.assert_collating_on(None); + m.assert_running_chains(&[2000.into()]); + + // This will send a CollateOn message to the same chain as the last CollateOn, + // but this is needed because that chain has been stopped + m.handle_update_assignment(Some(2000.into()), Some(2000.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + } + + #[test] + fn stop_collating_container_start_immediately() { + let mut m: MockContainerChainSpawner = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(2000.into()), None); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(None, None); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + + // This will start the chain already collating + m.handle_update_assignment(Some(2000.into()), Some(2000.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + } + + #[test] + fn stop_all_chains() { + let mut m: MockContainerChainSpawner = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(2000.into()), Some(2001.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into(), 2001.into()]); + + m.handle_update_assignment(None, None); + m.assert_collating_on(None); + m.assert_running_chains(&[]); + } + + #[test] + fn keep_collating_on_container() { + let mut m: MockContainerChainSpawner = MockContainerChainSpawner::new(); + + m.handle_update_assignment(Some(2000.into()), None); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(None, Some(2000.into())); + m.assert_collating_on(None); + m.assert_running_chains(&[2000.into()]); + + m.handle_update_assignment(Some(2000.into()), Some(2000.into())); + m.assert_collating_on(Some(2000.into())); + m.assert_running_chains(&[2000.into()]); + } + + #[test] + fn invalid_boot_nodes_are_ignored() { + let para_id = 100.into(); + let bootnode1 = + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9" + .to_vec(); + assert_eq!( + parse_boot_nodes_ignore_invalid(vec![b"A".to_vec()], para_id), + vec![] + ); + assert_eq!( + parse_boot_nodes_ignore_invalid(vec![b"\xff".to_vec()], para_id), + vec![] + ); + // Valid boot nodes are not ignored + assert_eq!( + parse_boot_nodes_ignore_invalid(vec![bootnode1], para_id).len(), + 1 + ); + } +} diff --git a/node/src/lib.rs b/node/src/lib.rs deleted file mode 100644 index f117b8a..0000000 --- a/node/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod chain_spec; -pub mod rpc; -pub mod service; diff --git a/node/src/main.rs b/node/src/main.rs index 426cbab..1565935 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -1,14 +1,33 @@ -//! Substrate Node Template CLI library. +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! Substrate Parachain Node Template CLI + #![warn(missing_docs)] mod chain_spec; -#[macro_use] -mod service; -mod benchmarking; mod cli; mod command; +mod container_chain_monitor; +mod container_chain_spawner; mod rpc; +mod service; +#[cfg(test)] +mod tests; fn main() -> sc_cli::Result<()> { - command::run() + command::run() } diff --git a/node/src/rpc.rs b/node/src/rpc.rs index e827041..3e2fdc9 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -1,3 +1,19 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + //! A collection of node-specific RPC methods. //! Substrate provides the `sc-rpc` crate, which defines the core RPC layer //! used by Substrate nodes. This file extends those RPC definitions with @@ -5,65 +21,96 @@ #![warn(missing_docs)] -use std::sync::Arc; - -use jsonrpsee::RpcModule; -use node_template_runtime::{opaque::Block, AccountId, Balance, Index}; -use sc_transaction_pool_api::TransactionPool; -use sp_api::ProvideRuntimeApi; -use sp_block_builder::BlockBuilder; -use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +pub use sc_rpc::DenyUnsafe; +use { + cumulus_primitives_core::ParaId, + dancebox_runtime::{opaque::Block, AccountId, Index as Nonce}, + manual_xcm_rpc::{ManualXcm, ManualXcmApiServer}, + polkadot_primitives::Hash, + sc_client_api::{AuxStore, UsageProvider}, + sc_consensus_manual_seal::{ + rpc::{ManualSeal, ManualSealApiServer}, + EngineCommand, + }, + sc_transaction_pool_api::TransactionPool, + services_payment_rpc::{ + ServicesPayment, ServicesPaymentApiServer as _, ServicesPaymentRuntimeApi, + }, + sp_api::ProvideRuntimeApi, + sp_block_builder::BlockBuilder, + sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}, + std::sync::Arc, + stream_payment_rpc::{StreamPayment, StreamPaymentApiServer as _, StreamPaymentRuntimeApi}, +}; -pub use sc_rpc_api::DenyUnsafe; +/// A type representing all RPC extensions. +pub type RpcExtension = jsonrpsee::RpcModule<()>; -/// Full client dependencies. +/// Full client dependencies pub struct FullDeps { - /// The client instance to use. - pub client: Arc, - /// Transaction pool instance. - pub pool: Arc

, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Whether to deny unsafe calls + pub deny_unsafe: DenyUnsafe, + /// Manual seal command sink + pub command_sink: Option>>, + /// Channels for manual xcm messages (downward, hrmp) + pub xcm_senders: Option<(flume::Sender>, flume::Sender<(ParaId, Vec)>)>, } -/// Instantiate all full RPC extensions. +/// Instantiate all RPC extensions. pub fn create_full( - deps: FullDeps, -) -> Result, Box> + deps: FullDeps, +) -> Result> where - C: ProvideRuntimeApi, - C: HeaderBackend + HeaderMetadata + 'static, - C: Send + Sync + 'static, - C::Api: substrate_frame_rpc_system::AccountNonceApi, - C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, - C::Api: profile_validation_runtime_api::ProfileValidationApi, - C::Api: department_funding_runtime_api::DepartmentFundingApi, - C::Api: positive_externality_runtime_api::PositiveExternalityApi, - C::Api: project_tips_runtime_api::ProjectTipsApi, - C::Api: BlockBuilder, - P: TransactionPool + 'static, + C: ProvideRuntimeApi + + HeaderBackend + + AuxStore + + HeaderMetadata + + Send + + Sync + + UsageProvider + + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi, + C::Api: BlockBuilder, + C::Api: StreamPaymentRuntimeApi, + C::Api: ServicesPaymentRuntimeApi, + P: TransactionPool + Sync + Send + 'static, { - use department_funding_rpc::DepartmentFundingApiServer; - use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; - use positive_externality_rpc::PositiveExternalityApiServer; - use profile_validation_rpc::ProfileValidationApiServer; - use project_tips_rpc::ProjectTipsApiServer; - use substrate_frame_rpc_system::{System, SystemApiServer}; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let mut module = RpcExtension::new(()); + let FullDeps { + client, + pool, + deny_unsafe, + command_sink, + xcm_senders, + } = deps; - let mut module = RpcModule::new(()); - let FullDeps { client, pool, deny_unsafe } = deps; + module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(StreamPayment::<_, Block>::new(client.clone()).into_rpc())?; + module.merge(ServicesPayment::<_, Block>::new(client).into_rpc())?; - module.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; - module.merge(TransactionPayment::new(client.clone()).into_rpc())?; - module.merge(profile_validation_rpc::ProfileValidation::new(client.clone()).into_rpc())?; - module.merge(department_funding_rpc::DepartmentFunding::new(client.clone()).into_rpc())?; - module.merge(positive_externality_rpc::PositiveExternality::new(client.clone()).into_rpc())?; - module.merge(project_tips_rpc::ProjectTips::new(client.clone()).into_rpc())?; + if let Some(command_sink) = command_sink { + module.merge( + // We provide the rpc handler with the sending end of the channel to allow the rpc + // send EngineCommands to the background block authorship task. + ManualSeal::new(command_sink).into_rpc(), + )?; + }; - // Extend this RPC with a custom API by using the following syntax. - // `YourRpcStruct` should have a reference to a client, which is needed - // to call into the runtime. - // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;` + if let Some((downward_message_channel, hrmp_message_channel)) = xcm_senders { + module.merge( + ManualXcm { + downward_message_channel, + hrmp_message_channel, + } + .into_rpc(), + )?; + } - Ok(module) + Ok(module) } diff --git a/node/src/service.rs b/node/src/service.rs index 723d1db..442f1dc 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -1,314 +1,1377 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. -use node_template_runtime::{self, opaque::Block, RuntimeApi}; -use sc_client_api::BlockBackend; -use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; -use sc_consensus_grandpa::SharedVoterState; -pub use sc_executor::NativeElseWasmExecutor; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; -use sc_telemetry::{Telemetry, TelemetryWorker}; -use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; -use std::{sync::Arc, time::Duration}; - -// Our native executor instance. -pub struct ExecutorDispatch; - -impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { - /// Only enable the benchmarking host functions when we actually want to benchmark. - #[cfg(feature = "runtime-benchmarks")] - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - /// Otherwise we only use the default Substrate host functions. - #[cfg(not(feature = "runtime-benchmarks"))] - type ExtendHostFunctions = (); - - fn dispatch(method: &str, data: &[u8]) -> Option> { - node_template_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - node_template_runtime::native_version() - } +use tokio_util::sync::CancellationToken; +#[allow(deprecated)] +use { + crate::{ + cli::ContainerChainCli, + container_chain_spawner::{CcSpawnMsg, ContainerChainSpawner}, + }, + cumulus_client_cli::CollatorOptions, + cumulus_client_collator::service::CollatorService, + cumulus_client_consensus_common::{ + ParachainBlockImport as TParachainBlockImport, ParachainBlockImportMarker, + }, + cumulus_client_consensus_proposer::Proposer, + cumulus_client_parachain_inherent::{MockValidationDataInherentDataProvider, MockXcmConfig}, + cumulus_client_service::{ + prepare_node_config, start_relay_chain_tasks, DARecoveryProfile, StartRelayChainTasksParams, + }, + cumulus_primitives_core::{ + relay_chain::{well_known_keys as RelayWellKnownKeys, CollatorPair}, + ParaId, + }, + cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}, + dancebox_runtime::{ + opaque::{Block, Hash}, + RuntimeApi, + }, + dc_orchestrator_chain_interface::{ + OrchestratorChainError, OrchestratorChainInterface, OrchestratorChainResult, PHash, PHeader, + }, + dp_slot_duration_runtime_api::TanssiSlotDurationApi, + futures::{Stream, StreamExt}, + nimbus_primitives::NimbusPair, + node_common::service::NodeBuilderConfig, + node_common::service::{ManualSealConfiguration, NodeBuilder, Sealing}, + pallet_registrar_runtime_api::RegistrarApi, + parity_scale_codec::Encode, + polkadot_cli::ProvideRuntimeApi, + polkadot_parachain_primitives::primitives::HeadData, + polkadot_service::Handle, + sc_basic_authorship::ProposerFactory, + sc_client_api::{ + AuxStore, Backend as BackendT, BlockchainEvents, HeaderBackend, UsageProvider, + }, + sc_consensus::{BasicQueue, BlockImport, ImportQueue}, + sc_executor::{NativeElseWasmExecutor, WasmExecutor}, + sc_network::NetworkBlock, + sc_network_sync::SyncingService, + sc_service::{Configuration, SpawnTaskHandle, TFullBackend, TFullClient, TaskManager}, + sc_telemetry::TelemetryHandle, + sc_transaction_pool::FullPool, + sp_api::StorageProof, + sp_consensus::{EnableProofRecording, SyncOracle}, + sp_consensus_slots::{Slot, SlotDuration}, + sp_core::{traits::SpawnEssentialNamed, H256}, + sp_keystore::KeystorePtr, + sp_state_machine::{Backend as StateBackend, StorageValue}, + std::{pin::Pin, sync::Arc, time::Duration}, + substrate_prometheus_endpoint::Registry, + tc_consensus::{ + collators::lookahead::{ + self as lookahead_tanssi_aura, Params as LookaheadTanssiAuraParams, + }, + OrchestratorAuraWorkerAuxData, + }, + tokio::sync::mpsc::{unbounded_channel, UnboundedSender}, +}; + +type FullBackend = TFullBackend; + +/// Native executor type. +pub struct ParachainNativeExecutor; + +impl sc_executor::NativeExecutionDispatch for ParachainNativeExecutor { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + dancebox_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + dancebox_runtime::native_version() + } } -pub(crate) type FullClient = - sc_service::TFullClient>; -type FullBackend = sc_service::TFullBackend; -type FullSelectChain = sc_consensus::LongestChain; - -pub fn new_partial( - config: &Configuration, -) -> Result< - sc_service::PartialComponents< - FullClient, - FullBackend, - FullSelectChain, - sc_consensus::DefaultImportQueue, - sc_transaction_pool::FullPool, - ( - sc_consensus_grandpa::GrandpaBlockImport< - FullBackend, - Block, - FullClient, - FullSelectChain, - >, - sc_consensus_grandpa::LinkHalf, - Option, - ), - >, - ServiceError, -> { - let telemetry = config - .telemetry_endpoints - .clone() - .filter(|x| !x.is_empty()) - .map(|endpoints| -> Result<_, sc_telemetry::Error> { - let worker = TelemetryWorker::new(16)?; - let telemetry = worker.handle().new_telemetry(endpoints); - Ok((worker, telemetry)) - }) - .transpose()?; - - let executor = sc_service::new_native_or_wasm_executor(&config); - - let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( - config, - telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), - executor, - )?; - let client = Arc::new(client); - - let telemetry = telemetry.map(|(worker, telemetry)| { - task_manager.spawn_handle().spawn("telemetry", None, worker.run()); - telemetry - }); - - let select_chain = sc_consensus::LongestChain::new(backend.clone()); - - let transaction_pool = sc_transaction_pool::BasicPool::new_full( - config.transaction_pool.clone(), - config.role.is_authority().into(), - config.prometheus_registry(), - task_manager.spawn_essential_handle(), - client.clone(), - ); - - let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( - client.clone(), - &(client.clone() as Arc<_>), - select_chain.clone(), - telemetry.as_ref().map(|x| x.handle()), - )?; - - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - - let import_queue = - sc_consensus_aura::import_queue::(ImportQueueParams { - block_import: grandpa_block_import.clone(), - justification_import: Some(Box::new(grandpa_block_import.clone())), - client: client.clone(), - create_inherent_data_providers: move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); - - Ok((slot, timestamp)) - }, - spawner: &task_manager.spawn_essential_handle(), - registry: config.prometheus_registry(), - check_for_equivocation: Default::default(), - telemetry: telemetry.as_ref().map(|x| x.handle()), - compatibility_mode: Default::default(), - })?; - - Ok(sc_service::PartialComponents { - client, - backend, - task_manager, - import_queue, - keystore_container, - select_chain, - transaction_pool, - other: (grandpa_block_import, grandpa_link, telemetry), - }) +pub struct NodeConfig; +impl NodeBuilderConfig for NodeConfig { + type Block = Block; + type RuntimeApi = RuntimeApi; + type ParachainExecutor = ParachainExecutor; +} + +pub struct ContainerChainNodeConfig; +impl NodeBuilderConfig for ContainerChainNodeConfig { + type Block = Block; + // TODO: RuntimeApi here should be the subset of runtime apis available for all containers + // Currently we are using the orchestrator runtime apis + type RuntimeApi = RuntimeApi; + type ParachainExecutor = ContainerChainExecutor; +} + +// Orchestrator chain types +type ParachainExecutor = NativeElseWasmExecutor; +pub type ParachainClient = TFullClient; +pub type ParachainBackend = TFullBackend; +type DevParachainBlockImport = OrchestratorParachainBlockImport>; +type ParachainBlockImport = TParachainBlockImport, ParachainBackend>; +type ParachainProposerFactory = + ProposerFactory, ParachainClient, EnableProofRecording>; + +// Container chains types +type ContainerChainExecutor = WasmExecutor; +pub type ContainerChainClient = TFullClient; +pub type ContainerChainBackend = ParachainBackend; +type ContainerChainBlockImport = + TParachainBlockImport, ContainerChainBackend>; + +thread_local!(static TIMESTAMP: std::cell::RefCell = const { std::cell::RefCell::new(0) }); + +/// Provide a mock duration starting at 0 in millisecond for timestamp inherent. +/// Each call will increment timestamp by slot_duration making Aura think time has passed. +struct MockTimestampInherentDataProvider; +#[async_trait::async_trait] +impl sp_inherents::InherentDataProvider for MockTimestampInherentDataProvider { + async fn provide_inherent_data( + &self, + inherent_data: &mut sp_inherents::InherentData, + ) -> Result<(), sp_inherents::Error> { + TIMESTAMP.with(|x| { + *x.borrow_mut() += dancebox_runtime::SLOT_DURATION; + inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x.borrow()) + }) + } + + async fn try_handle_error( + &self, + _identifier: &sp_inherents::InherentIdentifier, + _error: &[u8], + ) -> Option> { + // The pallet never reports error. + None + } +} + +/// Background task used to detect changes to container chain assignment, +/// and start/stop container chains on demand. The check runs on every new block. +pub fn build_check_assigned_para_id( + client: Arc, + sync_keystore: KeystorePtr, + cc_spawn_tx: UnboundedSender, + spawner: impl SpawnEssentialNamed, +) { + // Subscribe to new blocks in order to react to para id assignment + // This must be the stream of finalized blocks, otherwise the collators may rotate to a + // different chain before the block is finalized, and that could lead to a stalled chain + let mut import_notifications = client.finality_notification_stream(); + + let check_assigned_para_id_task = async move { + while let Some(msg) = import_notifications.next().await { + let block_hash = msg.hash; + let client_set_aside_for_cidp = client.clone(); + let sync_keystore = sync_keystore.clone(); + let cc_spawn_tx = cc_spawn_tx.clone(); + + check_assigned_para_id( + cc_spawn_tx, + sync_keystore, + client_set_aside_for_cidp, + block_hash, + ) + .unwrap(); + } + }; + + spawner.spawn_essential( + "check-assigned-para-id", + None, + Box::pin(check_assigned_para_id_task), + ); +} + +/// Check the parachain assignment using the orchestrator chain client, and send a `CcSpawnMsg` to +/// start or stop the required container chains. +/// +/// Checks the assignment for the next block, so if there is a session change on block 15, this will +/// detect the assignment change after importing block 14. +fn check_assigned_para_id( + cc_spawn_tx: UnboundedSender, + sync_keystore: KeystorePtr, + client_set_aside_for_cidp: Arc, + block_hash: H256, +) -> Result<(), Box> { + // Check current assignment + let current_container_chain_para_id = + tc_consensus::first_eligible_key::( + client_set_aside_for_cidp.as_ref(), + &block_hash, + sync_keystore.clone(), + ) + .map(|(_nimbus_key, para_id)| para_id); + + // Check assignment in the next session + let next_container_chain_para_id = + tc_consensus::first_eligible_key_next_session::( + client_set_aside_for_cidp.as_ref(), + &block_hash, + sync_keystore, + ) + .map(|(_nimbus_key, para_id)| para_id); + + cc_spawn_tx.send(CcSpawnMsg::UpdateAssignment { + current: current_container_chain_para_id, + next: next_container_chain_para_id, + })?; + + Ok(()) +} + +pub fn import_queue( + parachain_config: &Configuration, + node_builder: &NodeBuilder, +) -> (ParachainBlockImport, BasicQueue) { + // The nimbus import queue ONLY checks the signature correctness + // Any other checks corresponding to the author-correctness should be done + // in the runtime + let block_import = + ParachainBlockImport::new(node_builder.client.clone(), node_builder.backend.clone()); + + let import_queue = nimbus_consensus::import_queue( + node_builder.client.clone(), + block_import.clone(), + move |_, _| async move { + let time = sp_timestamp::InherentDataProvider::from_system_time(); + + Ok((time,)) + }, + &node_builder.task_manager.spawn_essential_handle(), + parachain_config.prometheus_registry(), + false, + ) + .expect("function never fails"); + + (block_import, import_queue) +} + +pub fn container_chain_import_queue( + parachain_config: &Configuration, + node_builder: &NodeBuilder, +) -> (ContainerChainBlockImport, BasicQueue) { + // The nimbus import queue ONLY checks the signature correctness + // Any other checks corresponding to the author-correctness should be done + // in the runtime + let block_import = + ContainerChainBlockImport::new(node_builder.client.clone(), node_builder.backend.clone()); + + let import_queue = nimbus_consensus::import_queue( + node_builder.client.clone(), + block_import.clone(), + move |_, _| async move { + let time = sp_timestamp::InherentDataProvider::from_system_time(); + + Ok((time,)) + }, + &node_builder.task_manager.spawn_essential_handle(), + parachain_config.prometheus_registry(), + false, + ) + .expect("function never fails"); + + (block_import, import_queue) +} + +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with("Orchestrator")] +async fn start_node_impl( + orchestrator_config: Configuration, + polkadot_config: Configuration, + mut container_chain_config: Option<(ContainerChainCli, tokio::runtime::Handle)>, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + let parachain_config = prepare_node_config(orchestrator_config); + if let Some((container_chain_cli, _)) = &mut container_chain_config { + // If the container chain args have no --wasmtime-precompiled flag, use the same as the orchestrator + if container_chain_cli + .base + .base + .import_params + .wasmtime_precompiled + .is_none() + { + container_chain_cli + .base + .base + .import_params + .wasmtime_precompiled = parachain_config.wasmtime_precompiled.clone(); + } + } + + let chain_type: sc_chain_spec::ChainType = parachain_config.chain_spec.chain_type(); + let relay_chain = crate::chain_spec::Extensions::try_get(&*parachain_config.chain_spec) + .map(|e| e.relay_chain.clone()) + .ok_or("Could not find relay_chain extension in chain-spec.")?; + + // Channel to send messages to start/stop container chains + let (cc_spawn_tx, cc_spawn_rx) = unbounded_channel(); + + // Create a `NodeBuilder` which helps setup parachain nodes common systems. + let mut node_builder = NodeConfig::new_builder(¶chain_config, hwbench.clone())?; + + let (block_import, import_queue) = import_queue(¶chain_config, &node_builder); + + let (relay_chain_interface, collator_key) = node_builder + .build_relay_chain_interface(¶chain_config, polkadot_config, collator_options.clone()) + .await?; + + let validator = parachain_config.role.is_authority(); + let force_authoring = parachain_config.force_authoring; + + let node_builder = node_builder + .build_cumulus_network( + ¶chain_config, + para_id, + import_queue, + relay_chain_interface.clone(), + ) + .await?; + + let rpc_builder = { + let client = node_builder.client.clone(); + let transaction_pool = node_builder.transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = crate::rpc::FullDeps { + client: client.clone(), + pool: transaction_pool.clone(), + deny_unsafe, + command_sink: None, + xcm_senders: None, + }; + + crate::rpc::create_full(deps).map_err(Into::into) + }) + }; + + let node_builder = node_builder.spawn_common_tasks(parachain_config, rpc_builder)?; + + let relay_chain_slot_duration = Duration::from_secs(6); + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + let sync_keystore = node_builder.keystore_container.keystore(); + let mut collate_on_tanssi: Arc< + dyn Fn() -> (CancellationToken, futures::channel::oneshot::Receiver<()>) + Send + Sync, + > = Arc::new(move || { + if validator { + panic!("Called uninitialized collate_on_tanssi"); + } else { + panic!("Called collate_on_tanssi when node is not running as a validator"); + } + }); + + let announce_block = { + let sync_service = node_builder.network.sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + + let (mut node_builder, import_queue_service) = node_builder.extract_import_queue_service(); + + start_relay_chain_tasks(StartRelayChainTasksParams { + client: node_builder.client.clone(), + announce_block: announce_block.clone(), + para_id, + relay_chain_interface: relay_chain_interface.clone(), + task_manager: &mut node_builder.task_manager, + da_recovery_profile: if validator { + DARecoveryProfile::Collator + } else { + DARecoveryProfile::FullNode + }, + import_queue: import_queue_service, + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle.clone()), + sync_service: node_builder.network.sync_service.clone(), + })?; + + if validator { + let collator_key = collator_key + .clone() + .expect("Command line arguments do not allow this. qed"); + + // Start task which detects para id assignment, and starts/stops container chains. + // Note that if this node was started without a `container_chain_config`, we don't + // support collation on container chains, so there is no need to detect changes to assignment + if container_chain_config.is_some() { + build_check_assigned_para_id( + node_builder.client.clone(), + sync_keystore.clone(), + cc_spawn_tx.clone(), + node_builder.task_manager.spawn_essential_handle(), + ); + } + + let start_collation = { + // Params for collate_on_tanssi closure + let node_spawn_handle = node_builder.task_manager.spawn_handle().clone(); + let node_keystore = node_builder.keystore_container.keystore().clone(); + let node_telemetry_handle = node_builder.telemetry.as_ref().map(|t| t.handle()).clone(); + let node_client = node_builder.client.clone(); + let node_backend = node_builder.backend.clone(); + let relay_interface = relay_chain_interface.clone(); + let node_sync_service = node_builder.network.sync_service.clone(); + let overseer = overseer_handle.clone(); + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + node_spawn_handle.clone(), + node_client.clone(), + node_builder.transaction_pool.clone(), + node_builder.prometheus_registry.as_ref(), + node_telemetry_handle.clone(), + ); + + move || { + start_consensus_orchestrator( + node_client.clone(), + node_backend.clone(), + block_import.clone(), + node_spawn_handle.clone(), + relay_interface.clone(), + node_sync_service.clone(), + node_keystore.clone(), + force_authoring, + relay_chain_slot_duration, + para_id, + collator_key.clone(), + overseer.clone(), + announce_block.clone(), + proposer_factory.clone(), + ) + } + }; + // Save callback for later, used when collator rotates from container chain back to orchestrator chain + collate_on_tanssi = Arc::new(start_collation); + } + + node_builder.network.start_network.start_network(); + + let sync_keystore = node_builder.keystore_container.keystore(); + let orchestrator_chain_interface_builder = OrchestratorChainInProcessInterfaceBuilder { + client: node_builder.client.clone(), + backend: node_builder.backend.clone(), + sync_oracle: node_builder.network.sync_service.clone(), + overseer_handle: overseer_handle.clone(), + }; + + if let Some((container_chain_cli, tokio_handle)) = container_chain_config { + // If the orchestrator chain is running as a full-node, we start a full node for the + // container chain immediately, because only collator nodes detect their container chain + // assignment so otherwise it will never start. + if !validator { + if let Some(container_chain_para_id) = container_chain_cli.base.para_id { + // Spawn new container chain node + cc_spawn_tx + .send(CcSpawnMsg::UpdateAssignment { + current: Some(container_chain_para_id.into()), + next: Some(container_chain_para_id.into()), + }) + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + } + } + + // Start container chain spawner task. This will start and stop container chains on demand. + let orchestrator_client = node_builder.client.clone(); + let spawn_handle = node_builder.task_manager.spawn_handle(); + let container_chain_spawner = ContainerChainSpawner { + orchestrator_chain_interface: orchestrator_chain_interface_builder.build(), + orchestrator_client, + container_chain_cli, + tokio_handle, + chain_type, + relay_chain, + relay_chain_interface, + collator_key, + sync_keystore, + orchestrator_para_id: para_id, + validator, + spawn_handle, + state: Default::default(), + collate_on_tanssi, + collation_cancellation_constructs: None, + }; + let state = container_chain_spawner.state.clone(); + + node_builder.task_manager.spawn_essential_handle().spawn( + "container-chain-spawner-rx-loop", + None, + container_chain_spawner.rx_loop(cc_spawn_rx, validator), + ); + + node_builder.task_manager.spawn_essential_handle().spawn( + "container-chain-spawner-debug-state", + None, + crate::container_chain_monitor::monitor_task(state), + ) + } + + Ok((node_builder.task_manager, node_builder.client)) +} + +// Log string that will be shown for the container chain: `[Container-2000]`. +// This needs to be a separate function because the `prefix_logs_with` macro +// has trouble parsing expressions. +fn container_log_str(para_id: ParaId) -> String { + format!("Container-{}", para_id) } -/// Builds a new service for a full client. -pub fn new_full(mut config: Configuration) -> Result { - let sc_service::PartialComponents { - client, - backend, - mut task_manager, - import_queue, - keystore_container, - select_chain, - transaction_pool, - other: (block_import, grandpa_link, mut telemetry), - } = new_partial(&config)?; - - let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( - &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), - &config.chain_spec, - ); - - config - .network - .extra_sets - .push(sc_consensus_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); - let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( - backend.clone(), - grandpa_link.shared_authority_set().clone(), - Vec::default(), - )); - - let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = - sc_service::build_network(sc_service::BuildNetworkParams { - config: &config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - spawn_handle: task_manager.spawn_handle(), - import_queue, - block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), - })?; - - if config.offchain_worker.enabled { - sc_service::build_offchain_workers( - &config, - task_manager.spawn_handle(), - client.clone(), - network.clone(), - ); - } - - let role = config.role.clone(); - let force_authoring = config.force_authoring; - let backoff_authoring_blocks: Option<()> = None; - let name = config.network.node_name.clone(); - let enable_grandpa = !config.disable_grandpa; - let prometheus_registry = config.prometheus_registry().cloned(); - - let rpc_extensions_builder = { - let client = client.clone(); - let pool = transaction_pool.clone(); - - Box::new(move |deny_unsafe, _| { - let deps = - crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; - crate::rpc::create_full(deps).map_err(Into::into) - }) - }; - - let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { - network: network.clone(), - client: client.clone(), - keystore: keystore_container.keystore(), - task_manager: &mut task_manager, - transaction_pool: transaction_pool.clone(), - rpc_builder: rpc_extensions_builder, - backend, - system_rpc_tx, - tx_handler_controller, - sync_service: sync_service.clone(), - config, - telemetry: telemetry.as_mut(), - })?; - - if role.is_authority() { - let proposer_factory = sc_basic_authorship::ProposerFactory::new( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|x| x.handle()), - ); - - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - - let aura = sc_consensus_aura::start_aura::( - StartAuraParams { - slot_duration, - client, - select_chain, - block_import, - proposer_factory, - create_inherent_data_providers: move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with(container_log_str(para_id))] +pub async fn start_node_impl_container( + parachain_config: Configuration, + orchestrator_client: Arc, + relay_chain_interface: Arc, + orchestrator_chain_interface: Arc, + collator_key: Option, + keystore: KeystorePtr, + para_id: ParaId, + orchestrator_para_id: ParaId, + collator: bool, +) -> sc_service::error::Result<( + TaskManager, + Arc, + Arc, +)> { + let parachain_config = prepare_node_config(parachain_config); + + // Create a `NodeBuilder` which helps setup parachain nodes common systems. + let node_builder = ContainerChainNodeConfig::new_builder(¶chain_config, None)?; + + let (block_import, import_queue) = + container_chain_import_queue(¶chain_config, &node_builder); + let import_queue_service = import_queue.service(); + + log::info!("are we collators? {:?}", collator); + let node_builder = node_builder + .build_cumulus_network( + ¶chain_config, + para_id, + import_queue, + relay_chain_interface.clone(), + ) + .await?; + + let force_authoring = parachain_config.force_authoring; + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + + let rpc_builder = { + let client = node_builder.client.clone(); + let transaction_pool = node_builder.transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = crate::rpc::FullDeps { + client: client.clone(), + pool: transaction_pool.clone(), + deny_unsafe, + command_sink: None, + xcm_senders: None, + }; + + crate::rpc::create_full(deps).map_err(Into::into) + }) + }; + + let node_builder = node_builder.spawn_common_tasks(parachain_config, rpc_builder)?; + + let announce_block = { + let sync_service = node_builder.network.sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + let (mut node_builder, _) = node_builder.extract_import_queue_service(); + + start_relay_chain_tasks(StartRelayChainTasksParams { + client: node_builder.client.clone(), + announce_block: announce_block.clone(), + para_id, + relay_chain_interface: relay_chain_interface.clone(), + task_manager: &mut node_builder.task_manager, + da_recovery_profile: if collator { + DARecoveryProfile::Collator + } else { + DARecoveryProfile::FullNode + }, + import_queue: import_queue_service, + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle.clone()), + sync_service: node_builder.network.sync_service.clone(), + })?; + + if collator { + let collator_key = collator_key + .clone() + .expect("Command line arguments do not allow this. qed"); + + let node_spawn_handle = node_builder.task_manager.spawn_handle().clone(); + let node_client = node_builder.client.clone(); + let node_backend = node_builder.backend.clone(); + + start_consensus_container( + node_client.clone(), + node_backend.clone(), + orchestrator_client.clone(), + block_import.clone(), + prometheus_registry.clone(), + node_builder.telemetry.as_ref().map(|t| t.handle()).clone(), + node_spawn_handle.clone(), + relay_chain_interface.clone(), + orchestrator_chain_interface.clone(), + node_builder.transaction_pool.clone(), + node_builder.network.sync_service.clone(), + keystore.clone(), + force_authoring, + relay_chain_slot_duration, + para_id, + orchestrator_para_id, + collator_key.clone(), + overseer_handle.clone(), + announce_block.clone(), + ); + } + + node_builder.network.start_network.start_network(); + + Ok(( + node_builder.task_manager, + node_builder.client, + node_builder.backend, + )) +} + +/// Build the import queue for the parachain runtime (manual seal). +fn build_manual_seal_import_queue( + _client: Arc, + block_import: DevParachainBlockImport, + config: &Configuration, + _telemetry: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> { + Ok(sc_consensus_manual_seal::import_queue( + Box::new(block_import), + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + )) +} + +#[sc_tracing::logging::prefix_logs_with(container_log_str(para_id))] +fn start_consensus_container( + client: Arc, + backend: Arc, + orchestrator_client: Arc, + block_import: ContainerChainBlockImport, + prometheus_registry: Option, + telemetry: Option, + spawner: SpawnTaskHandle, + relay_chain_interface: Arc, + orchestrator_chain_interface: Arc, + transaction_pool: Arc>, + sync_oracle: Arc>, + keystore: KeystorePtr, + force_authoring: bool, + relay_chain_slot_duration: Duration, + para_id: ParaId, + orchestrator_para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, +) { + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*orchestrator_client) + .expect("start_consensus_container: slot duration should exist"); + + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + spawner.clone(), + client.clone(), + transaction_pool, + prometheus_registry.as_ref(), + telemetry.clone(), + ); + + let proposer = Proposer::new(proposer_factory); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(spawner.clone()), + announce_block, + client.clone(), + ); + + let relay_chain_interace_for_cidp = relay_chain_interface.clone(); + let relay_chain_interace_for_orch = relay_chain_interface.clone(); + let orchestrator_client_for_cidp = orchestrator_client; + let client_for_cidp = client.clone(); + let client_for_hash_provider = client.clone(); + + let code_hash_provider = move |block_hash| { + client_for_hash_provider + .code_at(block_hash) + .ok() + .map(polkadot_primitives::ValidationCode) + .map(|c| c.hash()) + }; + + let params = LookaheadTanssiAuraParams { + create_inherent_data_providers: move |block_hash, (relay_parent, _validation_data)| { + let relay_chain_interface = relay_chain_interace_for_cidp.clone(); + let orchestrator_chain_interface = orchestrator_chain_interface.clone(); + let client = client_for_cidp.clone(); + + async move { + let authorities_noting_inherent = + ccp_authorities_noting_inherent::ContainerChainAuthoritiesInherentData::create_at( + relay_parent, + &relay_chain_interface, + &orchestrator_chain_interface, + orchestrator_para_id, + ) + .await; + + let slot_duration = { + // Default to 12s if runtime API does not exist + let slot_duration_ms = client + .runtime_api() + .slot_duration(block_hash) + .unwrap_or(12_000); + + SlotDuration::from_millis(slot_duration_ms) + }; + + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( *timestamp, slot_duration, ); - Ok((slot, timestamp)) - }, - force_authoring, - backoff_authoring_blocks, - keystore: keystore_container.keystore(), - sync_oracle: sync_service.clone(), - justification_sync_link: sync_service.clone(), - block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), - max_block_proposal_slot_portion: None, - telemetry: telemetry.as_ref().map(|x| x.handle()), - compatibility_mode: Default::default(), - }, - )?; - - // the AURA authoring task is considered essential, i.e. if it - // fails we take down the service with it. - task_manager - .spawn_essential_handle() - .spawn_blocking("aura", Some("block-authoring"), aura); - } - - if enable_grandpa { - // if the node isn't actively participating in consensus then it doesn't - // need a keystore, regardless of which protocol we use below. - let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; - - let grandpa_config = sc_consensus_grandpa::Config { - // FIXME #1578 make this available through chainspec - gossip_duration: Duration::from_millis(333), - justification_period: 512, - name: Some(name), - observer_enabled: false, - keystore, - local_role: role, - telemetry: telemetry.as_ref().map(|x| x.handle()), - protocol_name: grandpa_protocol_name, - }; - - // start the full GRANDPA voter - // NOTE: non-authorities could run the GRANDPA observer protocol, but at - // this point the full voter should provide better guarantees of block - // and vote data availability than the observer. The observer has not - // been tested extensively yet and having most nodes in a network run it - // could lead to finality stalls. - let grandpa_config = sc_consensus_grandpa::GrandpaParams { - config: grandpa_config, - link: grandpa_link, - network, - sync: Arc::new(sync_service), - voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), - prometheus_registry, - shared_voter_state: SharedVoterState::empty(), - telemetry: telemetry.as_ref().map(|x| x.handle()), - }; - - // the GRANDPA voter task is considered infallible, i.e. - // if it fails we take down the service with it. - task_manager.spawn_essential_handle().spawn_blocking( - "grandpa-voter", - None, - sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, - ); - } - - network_starter.start_network(); - Ok(task_manager) + let authorities_noting_inherent = authorities_noting_inherent.ok_or_else(|| { + Box::::from( + "Failed to create authoritiesnoting inherent", + ) + })?; + + Ok((slot, timestamp, authorities_noting_inherent)) + } + }, + get_orchestrator_aux_data: move |_block_hash, (relay_parent, _validation_data)| { + let relay_chain_interace_for_orch = relay_chain_interace_for_orch.clone(); + let orchestrator_client_for_cidp = orchestrator_client_for_cidp.clone(); + + async move { + let latest_header = + ccp_authorities_noting_inherent::ContainerChainAuthoritiesInherentData::get_latest_orchestrator_head_info( + relay_parent, + &relay_chain_interace_for_orch, + orchestrator_para_id, + ) + .await; + + let latest_header = latest_header.ok_or_else(|| { + Box::::from( + "Failed to fetch latest header", + ) + })?; + + let authorities = tc_consensus::authorities::( + orchestrator_client_for_cidp.as_ref(), + &latest_header.hash(), + para_id, + ); + + let authorities = authorities.ok_or_else(|| { + Box::::from( + "Failed to fetch authorities with error", + ) + })?; + + log::info!( + "Authorities {:?} found for header {:?}", + authorities, + latest_header + ); + + let min_slot_freq = tc_consensus::min_slot_freq::( + orchestrator_client_for_cidp.as_ref(), + &latest_header.hash(), + para_id, + ); + + let aux_data = OrchestratorAuraWorkerAuxData { + authorities, + min_slot_freq, + }; + + Ok(aux_data) + } + }, + block_import, + para_client: client, + relay_client: relay_chain_interface, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, + slot_duration, + force_authoring, + relay_chain_slot_duration, + proposer, + collator_service, + // Very limited proposal time. + authoring_duration: Duration::from_millis(500), + para_backend: backend, + code_hash_provider, + // This cancellation token is no-op as it is not shared outside. + cancellation_token: CancellationToken::new(), + }; + + let (fut, _exit_notification_receiver) = + lookahead_tanssi_aura::run::(params); + spawner.spawn("tanssi-aura-container", None, fut); +} + +/// Start collator task for orchestrator chain. +/// Returns a `CancellationToken` that can be used to cancel the collator task, +/// and a `oneshot::Receiver<()>` that can be used to wait until the task has ended. +fn start_consensus_orchestrator( + client: Arc, + backend: Arc, + block_import: ParachainBlockImport, + spawner: SpawnTaskHandle, + relay_chain_interface: Arc, + sync_oracle: Arc>, + keystore: KeystorePtr, + force_authoring: bool, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + proposer_factory: ParachainProposerFactory, +) -> (CancellationToken, futures::channel::oneshot::Receiver<()>) { + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client) + .expect("start_consensus_orchestrator: slot duration should exist"); + + let proposer = Proposer::new(proposer_factory); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(spawner.clone()), + announce_block, + client.clone(), + ); + + let relay_chain_interace_for_cidp = relay_chain_interface.clone(); + let client_set_aside_for_cidp = client.clone(); + let client_set_aside_for_orch = client.clone(); + let client_for_hash_provider = client.clone(); + + let code_hash_provider = move |block_hash| { + client_for_hash_provider + .code_at(block_hash) + .ok() + .map(polkadot_primitives::ValidationCode) + .map(|c| c.hash()) + }; + + let cancellation_token = CancellationToken::new(); + + let params = LookaheadTanssiAuraParams { + create_inherent_data_providers: move |block_hash, (relay_parent, _validation_data)| { + let relay_chain_interface = relay_chain_interace_for_cidp.clone(); + let client_set_aside_for_cidp = client_set_aside_for_cidp.clone(); + async move { + let para_ids = client_set_aside_for_cidp + .runtime_api() + .registered_paras(block_hash)?; + let para_ids: Vec<_> = para_ids.into_iter().collect(); + let author_noting_inherent = + tp_author_noting_inherent::OwnParachainInherentData::create_at( + relay_parent, + &relay_chain_interface, + ¶_ids, + ) + .await; + + // Fetch duration every block to avoid downtime when passing from 12 to 6s + let slot_duration = sc_consensus_aura::standalone::slot_duration_at( + &*client_set_aside_for_cidp.clone(), + block_hash, + ) + .expect("Slot duration should be set"); + + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + let author_noting_inherent = author_noting_inherent.ok_or_else(|| { + Box::::from( + "Failed to create author noting inherent", + ) + })?; + + Ok((slot, timestamp, author_noting_inherent)) + } + }, + get_orchestrator_aux_data: move |block_hash: H256, (_relay_parent, _validation_data)| { + let client_set_aside_for_orch = client_set_aside_for_orch.clone(); + + async move { + let authorities = tc_consensus::authorities::( + client_set_aside_for_orch.as_ref(), + &block_hash, + para_id, + ); + + let authorities = authorities.ok_or_else(|| { + Box::::from( + "Failed to fetch authorities with error", + ) + })?; + + log::info!( + "Authorities {:?} found for header {:?}", + authorities, + block_hash + ); + + let aux_data = OrchestratorAuraWorkerAuxData { + authorities, + // This is the orchestrator consensus, it does not have a slot frequency + min_slot_freq: None, + }; + + Ok(aux_data) + } + }, + block_import, + para_client: client, + relay_client: relay_chain_interface, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, + slot_duration, + relay_chain_slot_duration, + force_authoring, + proposer, + collator_service, + // Very limited proposal time. + authoring_duration: Duration::from_millis(500), + code_hash_provider, + para_backend: backend, + cancellation_token: cancellation_token.clone(), + }; + + let (fut, exit_notification_receiver) = + lookahead_tanssi_aura::run::(params); + spawner.spawn("tanssi-aura", None, fut); + + (cancellation_token, exit_notification_receiver) +} + +/// Start a parachain node. +pub async fn start_parachain_node( + parachain_config: Configuration, + polkadot_config: Configuration, + container_config: Option<(ContainerChainCli, tokio::runtime::Handle)>, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + start_node_impl( + parachain_config, + polkadot_config, + container_config, + collator_options, + para_id, + hwbench, + ) + .await +} + +pub const SOFT_DEADLINE_PERCENT: sp_runtime::Percent = sp_runtime::Percent::from_percent(100); + +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with("Orchestrator Dev Node")] +pub fn start_dev_node( + orchestrator_config: Configuration, + sealing: Sealing, + hwbench: Option, + para_id: ParaId, +) -> sc_service::error::Result { + let parachain_config = prepare_node_config(orchestrator_config); + + // Create a `NodeBuilder` which helps setup parachain nodes common systems. + let node_builder = NodeConfig::new_builder(¶chain_config, hwbench)?; + + // This node block import. + let block_import = DevParachainBlockImport::new(node_builder.client.clone()); + let import_queue = build_manual_seal_import_queue( + node_builder.client.clone(), + block_import.clone(), + ¶chain_config, + node_builder + .telemetry + .as_ref() + .map(|telemetry| telemetry.handle()), + &node_builder.task_manager, + )?; + + // Build a Substrate Network. (not cumulus since it is a dev node, it mocks + // the relaychain) + let mut node_builder = node_builder.build_substrate_network(¶chain_config, import_queue)?; + + // If we're running a collator dev node we must install manual seal block + // production. + let mut command_sink = None; + let mut xcm_senders = None; + if parachain_config.role.is_authority() { + let client = node_builder.client.clone(); + let (downward_xcm_sender, downward_xcm_receiver) = flume::bounded::>(100); + let (hrmp_xcm_sender, hrmp_xcm_receiver) = flume::bounded::<(ParaId, Vec)>(100); + xcm_senders = Some((downward_xcm_sender, hrmp_xcm_sender)); + + command_sink = node_builder.install_manual_seal(ManualSealConfiguration { + block_import, + sealing, + soft_deadline: Some(SOFT_DEADLINE_PERCENT), + select_chain: sc_consensus::LongestChain::new(node_builder.backend.clone()), + consensus_data_provider: Some(Box::new( + tc_consensus::OrchestratorManualSealAuraConsensusDataProvider::new( + node_builder.client.clone(), + node_builder.keystore_container.keystore(), + para_id, + ), + )), + create_inherent_data_providers: move |block: H256, ()| { + let current_para_block = client + .number(block) + .expect("Header lookup should succeed") + .expect("Header passed in as parent should be present in backend."); + + let para_ids = client + .runtime_api() + .registered_paras(block) + .expect("registered_paras runtime API should exist") + .into_iter() + .collect(); + + let hash = client + .hash(current_para_block.saturating_sub(1)) + .expect("Hash of the desired block must be present") + .expect("Hash of the desired block should exist"); + + let para_header = client + .expect_header(hash) + .expect("Expected parachain header should exist") + .encode(); + + let para_head_data = HeadData(para_header).encode(); + let para_head_key = RelayWellKnownKeys::para_head(para_id); + let relay_slot_key = RelayWellKnownKeys::CURRENT_SLOT.to_vec(); + + let slot_duration = sc_consensus_aura::standalone::slot_duration_at( + &*client.clone(), + block, + ).expect("Slot duration should be set"); + + let mut timestamp = 0u64; + TIMESTAMP.with(|x| { + timestamp = x.clone().take(); + }); + + timestamp += dancebox_runtime::SLOT_DURATION; + let relay_slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + timestamp.into(), + slot_duration, + ); + let relay_slot = u64::from(*relay_slot); + + let downward_xcm_receiver = downward_xcm_receiver.clone(); + let hrmp_xcm_receiver = hrmp_xcm_receiver.clone(); + + let client_for_xcm = client.clone(); + async move { + let mocked_author_noting = + tp_author_noting_inherent::MockAuthorNotingInherentDataProvider { + current_para_block, + relay_offset: 1000, + relay_blocks_per_para_block: 2, + para_ids, + slots_per_para_block: 1, + }; + let mut additional_keys = mocked_author_noting.get_key_values(); + additional_keys.append(&mut vec![(para_head_key, para_head_data), (relay_slot_key, Slot::from(relay_slot).encode())]); + + let time = MockTimestampInherentDataProvider; + let mocked_parachain = MockValidationDataInherentDataProvider { + current_para_block, + relay_offset: 1000, + relay_blocks_per_para_block: 2, + // TODO: Recheck + para_blocks_per_relay_epoch: 10, + relay_randomness_config: (), + xcm_config: MockXcmConfig::new( + &*client_for_xcm, + block, + para_id, + Default::default(), + ), + raw_downward_messages: downward_xcm_receiver.drain().collect(), + raw_horizontal_messages: hrmp_xcm_receiver.drain().collect(), + additional_key_values: Some(additional_keys), + }; + + Ok((time, mocked_parachain, mocked_author_noting)) + } + }, + })?; + } + + // This node RPC builder. + let rpc_builder = { + let client = node_builder.client.clone(); + let transaction_pool = node_builder.transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = crate::rpc::FullDeps { + client: client.clone(), + pool: transaction_pool.clone(), + deny_unsafe, + command_sink: command_sink.clone(), + xcm_senders: xcm_senders.clone(), + }; + + crate::rpc::create_full(deps).map_err(Into::into) + }) + }; + + // We spawn all the common substrate tasks to properly run a node. + let node_builder = node_builder.spawn_common_tasks(parachain_config, rpc_builder)?; + + log::info!("Development Service Ready"); + + // We start the networking part. + node_builder.network.start_network.start_network(); + + Ok(node_builder.task_manager) +} + +/// Can be called for a `Configuration` to check if it is a configuration for +/// the orchestrator network. +pub trait IdentifyVariant { + /// Returns `true` if this is a configuration for a dev network. + fn is_dev(&self) -> bool; +} + +impl IdentifyVariant for Box { + fn is_dev(&self) -> bool { + self.chain_type() == sc_chain_spec::ChainType::Development + } +} + +/// Orchestrator Parachain Block import. We cannot use the one in cumulus as it overrides the best +/// chain selection rule +#[derive(Clone)] +pub struct OrchestratorParachainBlockImport { + inner: BI, +} + +impl OrchestratorParachainBlockImport { + /// Create a new instance. + pub fn new(inner: BI) -> Self { + Self { inner } + } +} + +/// We simply rely on the inner +#[async_trait::async_trait] +impl BlockImport for OrchestratorParachainBlockImport +where + BI: BlockImport + Send, +{ + type Error = BI::Error; + + async fn check_block( + &mut self, + block: sc_consensus::BlockCheckParams, + ) -> Result { + self.inner.check_block(block).await + } + + async fn import_block( + &mut self, + params: sc_consensus::BlockImportParams, + ) -> Result { + let res = self.inner.import_block(params).await?; + + Ok(res) + } +} + +/// But we need to implement the ParachainBlockImportMarker trait to fullfil +impl ParachainBlockImportMarker for OrchestratorParachainBlockImport {} + +/// Builder for a concrete relay chain interface, created from a full node. Builds +/// a [`RelayChainInProcessInterface`] to access relay chain data necessary for parachain operation. +/// +/// The builder takes a [`polkadot_client::Client`] +/// that wraps a concrete instance. By using [`polkadot_client::ExecuteWithClient`] +/// the builder gets access to this concrete instance and instantiates a [`RelayChainInProcessInterface`] with it. +struct OrchestratorChainInProcessInterfaceBuilder { + client: Arc, + backend: Arc, + sync_oracle: Arc, + overseer_handle: Handle, +} + +impl OrchestratorChainInProcessInterfaceBuilder { + pub fn build(self) -> Arc { + Arc::new(OrchestratorChainInProcessInterface::new( + self.client, + self.backend, + self.sync_oracle, + self.overseer_handle, + )) + } +} + +/// Provides an implementation of the [`RelayChainInterface`] using a local in-process relay chain node. +pub struct OrchestratorChainInProcessInterface { + pub full_client: Arc, + pub backend: Arc, + pub sync_oracle: Arc, + pub overseer_handle: Handle, +} + +impl OrchestratorChainInProcessInterface { + /// Create a new instance of [`RelayChainInProcessInterface`] + pub fn new( + full_client: Arc, + backend: Arc, + sync_oracle: Arc, + overseer_handle: Handle, + ) -> Self { + Self { + full_client, + backend, + sync_oracle, + overseer_handle, + } + } +} + +impl Clone for OrchestratorChainInProcessInterface { + fn clone(&self) -> Self { + Self { + full_client: self.full_client.clone(), + backend: self.backend.clone(), + sync_oracle: self.sync_oracle.clone(), + overseer_handle: self.overseer_handle.clone(), + } + } +} + +#[async_trait::async_trait] +impl OrchestratorChainInterface for OrchestratorChainInProcessInterface +where + Client: ProvideRuntimeApi + + BlockchainEvents + + AuxStore + + UsageProvider + + Sync + + Send, +{ + async fn get_storage_by_key( + &self, + orchestrator_parent: PHash, + key: &[u8], + ) -> OrchestratorChainResult> { + let state = self.backend.state_at(orchestrator_parent)?; + state + .storage(key) + .map_err(OrchestratorChainError::GenericError) + } + + async fn prove_read( + &self, + orchestrator_parent: PHash, + relevant_keys: &[Vec], + ) -> OrchestratorChainResult { + let state_backend = self.backend.state_at(orchestrator_parent)?; + + sp_state_machine::prove_read(state_backend, relevant_keys) + .map_err(OrchestratorChainError::StateMachineError) + } + + fn overseer_handle(&self) -> OrchestratorChainResult { + Ok(self.overseer_handle.clone()) + } + + /// Get a stream of import block notifications. + async fn import_notification_stream( + &self, + ) -> OrchestratorChainResult + Send>>> { + let notification_stream = self + .full_client + .import_notification_stream() + .map(|notification| notification.header); + Ok(Box::pin(notification_stream)) + } + + /// Get a stream of new best block notifications. + async fn new_best_notification_stream( + &self, + ) -> OrchestratorChainResult + Send>>> { + let notifications_stream = + self.full_client + .import_notification_stream() + .filter_map(|notification| async move { + notification.is_new_best.then_some(notification.header) + }); + Ok(Box::pin(notifications_stream)) + } + + /// Get a stream of finality notifications. + async fn finality_notification_stream( + &self, + ) -> OrchestratorChainResult + Send>>> { + let notification_stream = self + .full_client + .finality_notification_stream() + .map(|notification| notification.header); + Ok(Box::pin(notification_stream)) + } } diff --git a/node/src/tests/mod.rs b/node/src/tests/mod.rs new file mode 100644 index 0000000..ebd43bf --- /dev/null +++ b/node/src/tests/mod.rs @@ -0,0 +1,118 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! High level node tests, similar to spawning `tanssi-node --dev` and inspecting output logs. + +use { + crate::cli::Cli, + sc_cli::{Runner, SubstrateCli}, + sc_service::TaskManager, + std::time::Duration, +}; + +mod panics; + +// Create a runner for tests +fn create_runner() -> Runner { + // tanssi-node args should go here, `--dev` is probably enough + let cli = Cli::from_iter(["--dev"]); + let runner = cli.create_runner(&cli.run.normalize()).unwrap(); + + runner +} + +// Nice hack from polkadot-sdk to run a unit test in a separate process. +// We need to use this because create_runner sets up logging and a new panic hook, and that is +// global and fails if it was already setup by a previous test. +// Improved from upstream by using the exact test name, and by never capturing the test output. +fn run_test_in_another_process( + test_name: &str, + test_body: impl FnOnce(), +) -> Option { + run_test_in_another_process_expect_error(test_name, 0, test_body) +} + +fn run_test_in_another_process_expect_error( + test_name: &str, + exit_code: i32, + test_body: impl FnOnce(), +) -> Option { + if std::env::var("RUN_FORKED_TEST").is_ok() { + test_body(); + None + } else { + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .arg(test_name) + .arg("--exact") + .arg("--nocapture") + .arg("--include-ignored") + .env("RUN_FORKED_TEST", "1") + .output() + .unwrap(); + + assert_eq!(output.status.code(), Some(exit_code)); + Some(output) + } +} + +/// Macro to get the name of the current function at runtime. Used to make calling +/// `run_test_in_another_process` less error-prone. Copied from `stdext`, but modified to remove +/// the binary name from the output. +// https://github.com/popzxc/stdext-rs/blob/dc03b4afa28b3a1d2451ca54ad252244f029099b/src/macros.rs#L63 +#[macro_export] +macro_rules! function_name { + () => {{ + // Okay, this is ugly, I get it. However, this is the best we can get on a stable rust. + fn f() {} + fn type_name_of(_: T) -> &'static str { + std::any::type_name::() + } + let name = type_name_of(f); + // `3` is the length of the `::f`. + let name = &name[..name.len() - 3]; + // Strip initial tanssi_node:: + let end_of_first_item = name.bytes().position(|x| x == b':').unwrap(); + // `2` is the length of the `::` after `tanssi_node` + &name[end_of_first_item + 2..] + }}; +} + +#[test] +fn function_name_works() { + assert_eq!(function_name!(), "tests::function_name_works"); +} + +#[test] +fn run_test_in_another_process_works() { + let parent_pid = std::process::id(); + let output = run_test_in_another_process(function_name!(), || { + let child_pid = std::process::id(); + eprintln!("Child process running, PID: {}.", child_pid); + }); + + if output.is_none() { + // Assert that the output is only None if we are the child process + assert!(std::env::var("RUN_FORKED_TEST").is_ok()); + } + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + + assert!(stderr.contains("Child process running, PID: ")); + // Assert child process id is different from parent process id + assert!(!stderr.contains(&format!("PID: {}.", parent_pid))); +} diff --git a/node/src/tests/panics.rs b/node/src/tests/panics.rs new file mode 100644 index 0000000..826ac2c --- /dev/null +++ b/node/src/tests/panics.rs @@ -0,0 +1,283 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see . + +//! Tests related to panics: which ones stop the node, which ones do not, which tasks are essential, +//! etc. + +use {super::*, crate::function_name}; + +// This test is from sc_cli: +// https://github.com/paritytech/polkadot-sdk/blob/39b1f50f1c251def87c1625d68567ed252dc6272/substrate/client/cli/src/runner.rs#L363 +/// This test ensures that `run_node_until_exit` aborts waiting for "stuck" tasks after 60 +/// seconds, aka doesn't wait until they are finished (which may never happen). +#[test] +#[ignore = "takes 60 seconds to run"] +fn ensure_run_until_exit_is_not_blocking_indefinitely() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + let (sender, receiver) = futures::channel::oneshot::channel(); + + // We need to use `spawn_blocking` here so that we get a dedicated thread + // for our future. This future is more blocking code that will never end. + task_manager + .spawn_handle() + .spawn_blocking("test", None, async move { + let _ = sender.send(()); + loop { + std::thread::sleep(Duration::from_secs(30)); + } + }); + + task_manager + .spawn_essential_handle() + .spawn_blocking("test2", None, async { + // Let's stop this essential task directly when our other task + // started. It will signal that the task manager should end. + let _ = receiver.await; + }); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap(); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + + assert!(stderr.contains("Task \"test\" was still running after waiting 60 seconds to finish.")); + assert!( + !stderr.contains("Task \"test2\" was still running after waiting 60 seconds to finish.") + ); +} + +#[test] +fn node_stops_if_blocking_task_panics() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + + task_manager + .spawn_handle() + .spawn_blocking("test", None, async move { + panic!("spawn_blocking panicked"); + }); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap(); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + + assert!(stderr.contains("Thread 'tokio-runtime-worker' panicked at 'spawn_blocking panicked',")); +} + +#[test] +fn node_stops_if_non_essential_task_panics() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + + task_manager.spawn_handle().spawn("test", None, async move { + panic!("non-essential task panicked"); + }); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap(); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + + assert!( + stderr.contains("Thread 'tokio-runtime-worker' panicked at 'non-essential task panicked',") + ); +} + +#[test] +fn node_stops_if_essential_task_panics() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + + task_manager + .spawn_essential_handle() + .spawn("test", None, async move { + panic!("essential task panicked"); + }); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap(); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + + assert!(stderr.contains("Thread 'tokio-runtime-worker' panicked at 'essential task panicked',")); +} + +#[test] +fn node_stops_if_essential_task_finishes() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + + task_manager + .spawn_essential_handle() + .spawn("test", None, async move { + // Sleep for 2 seconds and return. + // An essential task that returns should cause the task manager to stop. + tokio::time::sleep(Duration::from_secs(2)).await; + }); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap(); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + + assert!(stderr.contains("Essential task `test` failed. Shutting down service.")); +} + +#[test] +fn node_stops_if_rust_thread_panics() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + + std::thread::spawn(|| panic!("rust thread panicked")); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap_err(); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + assert!(stderr.contains("Thread '' panicked at 'rust thread panicked',")); +} + +#[test] +#[ignore = "takes 10 seconds to run"] +fn node_does_not_stop_if_non_essential_task_finishes() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + + task_manager + .spawn_handle() + .spawn("test1", None, async move { + // Sleep for 2 seconds and return. + // A non-essential task that returns should not cause the task manager to stop. + tokio::time::sleep(Duration::from_secs(2)).await; + }); + + task_manager + .spawn_essential_handle() + .spawn("test2", None, async move { + // Sleep for 10 seconds and return. + // An essential task that returns should cause the task manager to stop. + // Therefore this node should stop after 10 seconds. + tokio::time::sleep(Duration::from_secs(10)).await; + }); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap(); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + + assert!(stderr.contains("Essential task `test2` failed. Shutting down service.")); + assert!(!stderr.contains("test1")); +} + +#[test] +fn catch_unwind_example() { + let output = run_test_in_another_process_expect_error(function_name!(), 1, || { + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + + // Because of the custom panic hook set by create_runner, using catch_unwind is + // only possible in a single-threaded context after calling force_unwind. + { + let _guard = sp_panic_handler::AbortGuard::force_unwind(); + + std::panic::catch_unwind(|| { + panic!("First panic did not stop the node"); + }) + .unwrap_err(); + } + + // We dropped the guard, the default behavior is to abort + std::panic::catch_unwind(|| { + panic!("Second panic should not stop the node, but it does"); + }) + .unwrap_err(); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap(); + + panic!("Third panic, unreachable"); + }); + + let Some(output) = output else { return }; + + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); + assert!(stderr.contains(" panicked at 'First panic did not stop the node',")); + assert!(stderr.contains(" panicked at 'Second panic should not stop the node, but it does',")); + assert!(!stderr.contains("Third panic, unreachable")); + assert_eq!(stderr.matches(" panicked at ").count(), 2) +} diff --git a/pallets/author-noting/Cargo.toml b/pallets/author-noting/Cargo.toml new file mode 100644 index 0000000..add6588 --- /dev/null +++ b/pallets/author-noting/Cargo.toml @@ -0,0 +1,107 @@ +[package] +name = "pallet-author-noting" +authors = { workspace = true } +description = "Author noting pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +hex = { workspace = true, optional = true, features = [ "alloc" ] } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive", "max-encoded-len" ] } +scale-info = { workspace = true } +serde = { workspace = true, optional = true, features = [ "derive" ] } +sp-consensus-aura = { workspace = true } +sp-core = { workspace = true } +sp-inherents = { workspace = true } +sp-runtime = { workspace = true } +sp-state-machine = { workspace = true } +sp-std = { workspace = true } +sp-trie = { workspace = true } + +cumulus-pallet-parachain-system = { workspace = true } +cumulus-primitives-core = { workspace = true } +dp-chain-state-snapshot = { workspace = true } +dp-core = { workspace = true } +nimbus-primitives = { workspace = true } +tp-author-noting-inherent = { workspace = true } +tp-traits = { workspace = true } + +[dev-dependencies] +bounded-collections = { workspace = true } +hex-literal = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-primitives = { workspace = true } +sp-externalities = { workspace = true } +sp-io = { workspace = true } +sp-state-machine = { workspace = true } +sp-version = { workspace = true } +test-relay-sproof-builder = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "bounded-collections/std", + "cumulus-pallet-parachain-system/std", + "cumulus-primitives-core/std", + "cumulus-primitives-core/std", + "dp-chain-state-snapshot/std", + "dp-core/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "hex", + "hex?/std", + "log/std", + "nimbus-primitives/std", + "parity-scale-codec/std", + "polkadot-parachain-primitives/std", + "polkadot-primitives/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-externalities/std", + "sp-inherents/std", + "sp-io/std", + "sp-runtime/std", + "sp-state-machine/std", + "sp-std/std", + "sp-trie/std", + "sp-version/std", + "test-relay-sproof-builder/std", + "tp-author-noting-inherent/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "hex", + "nimbus-primitives/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "cumulus-pallet-parachain-system/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "nimbus-primitives/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/author-noting/rpc/runtime-api/Cargo.toml b/pallets/author-noting/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000..9754f31 --- /dev/null +++ b/pallets/author-noting/rpc/runtime-api/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "pallet-author-noting-runtime-api" +authors = { workspace = true } +description = "Runtime API definition of pallet-author-noting" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +parity-scale-codec = { workspace = true } +sp-api = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "parity-scale-codec/std", + "sp-api/std", +] diff --git a/pallets/author-noting/rpc/runtime-api/src/lib.rs b/pallets/author-noting/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000..a33aec4 --- /dev/null +++ b/pallets/author-noting/rpc/runtime-api/src/lib.rs @@ -0,0 +1,31 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Runtime API for Author Noting pallet + +#![cfg_attr(not(feature = "std"), no_std)] + +sp_api::decl_runtime_apis! { + pub trait AuthorNotingApi + where + AccountId: parity_scale_codec::Codec, + BlockNumber: parity_scale_codec::Codec, + ParaId: parity_scale_codec::Codec, + { + fn latest_block_number(para_id: ParaId) -> Option; + fn latest_author(para_id: ParaId) -> Option; + } +} diff --git a/pallets/author-noting/src/benchmarks.rs b/pallets/author-noting/src/benchmarks.rs new file mode 100644 index 0000000..b721eae --- /dev/null +++ b/pallets/author-noting/src/benchmarks.rs @@ -0,0 +1,105 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#![cfg(feature = "runtime-benchmarks")] + +//! Benchmarking +use { + crate::{Call, Config, Pallet}, + cumulus_pallet_parachain_system::RelaychainStateProvider, + frame_benchmarking::{account, benchmarks}, + frame_support::assert_ok, + frame_system::RawOrigin, + sp_std::vec, + tp_traits::{GetContainerChainAuthor, GetCurrentContainerChains}, +}; + +mod test_sproof { + use sp_trie::StorageProof; + + /// Mocked proof because we cannot build proofs in a no-std environment. + /// Only stores the number of parachains, and reads a previously encoded proof for that number + /// of items from `crate::mock_proof`. + #[derive(Clone, Default)] + pub struct ParaHeaderSproofBuilder { + pub num_items: usize, + } + + impl ParaHeaderSproofBuilder { + pub fn into_state_root_and_proof( + self, + ) -> (cumulus_primitives_core::relay_chain::Hash, StorageProof) { + let encoded = crate::mock_proof::ENCODED_PROOFS[self.num_items]; + + let root = hex::decode(encoded.1).unwrap(); + let proof = StorageProof::new(encoded.2.iter().map(|s| hex::decode(s).unwrap())); + + (<[u8; 32]>::try_from(root).unwrap().into(), proof) + } + } +} + +benchmarks! { + set_latest_author_data { + // Depend on the number of parachains registered + let x in 0..100; + + let mut sproof_builder = test_sproof::ParaHeaderSproofBuilder::default(); + let mut container_chains = vec![]; + + for para_id in 0..x { + let para_id = para_id.into(); + container_chains.push(para_id); + // Mock assigned authors for this para id + let author: T::AccountId = account("account id", 0u32, 0u32); + // Use the max allowed value for num_each_container_chain + let num_each_container_chain = 2; + T::ContainerChainAuthor::set_authors_for_para_id(para_id, vec![author; num_each_container_chain]); + sproof_builder.num_items += 1; + } + + let (root, proof) = sproof_builder.into_state_root_and_proof(); + + let data = tp_author_noting_inherent::OwnParachainInherentData { + relay_storage_proof: proof, + }; + + T::ContainerChains::set_current_container_chains(&container_chains); + T::RelayChainStateProvider::set_current_relay_chain_state(cumulus_pallet_parachain_system::RelayChainState { + state_root: root, + number: 0, + }); + }: _(RawOrigin::None, data) + + set_author { + let para_id = 1000.into(); + let block_number = 1; + let author: T::AccountId = account("account id", 0u32, 0u32); + }: _(RawOrigin::Root, para_id, block_number, author, (block_number as u64).into()) + + kill_author_data { + let para_id = 1000.into(); + let block_number = 1; + let author: T::AccountId = account("account id", 0u32, 0u32); + assert_ok!(Pallet::::set_author(RawOrigin::Root.into(), para_id, block_number, author, (block_number as u64).into())); + }: _(RawOrigin::Root, para_id) + + impl_benchmark_test_suite!( + Pallet, + crate::mock::new_test_ext(), + crate::mock::Test + ); +} diff --git a/pallets/author-noting/src/lib.rs b/pallets/author-noting/src/lib.rs new file mode 100644 index 0000000..31ece32 --- /dev/null +++ b/pallets/author-noting/src/lib.rs @@ -0,0 +1,435 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Author Noting Pallet +//! +//! This pallet notes the author of the different containerChains that have registered: +//! +//! The set of container chains is retrieved thanks to the GetContainerChains trait +//! For each containerChain, we inspect the Header stored in the relayChain as +//! a generic header. This is the first requirement for containerChains. +//! +//! The second requirement is that an Aura digest with the slot number for the containerChains +//! needs to exist +//! +//! Using those two requirements we can select who the author was based on the collators assigned +//! to that containerChain, by simply assigning the slot position. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use dp_chain_state_snapshot::*; +use { + cumulus_pallet_parachain_system::RelaychainStateProvider, + cumulus_primitives_core::{ + relay_chain::{BlakeTwo256, BlockNumber, HeadData}, + ParaId, + }, + dp_core::well_known_keys::PARAS_HEADS_INDEX, + frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*, Hashable}, + frame_system::pallet_prelude::*, + nimbus_primitives::SlotBeacon, + parity_scale_codec::{Decode, Encode}, + sp_consensus_aura::{inherents::InherentType, Slot, AURA_ENGINE_ID}, + sp_inherents::{InherentIdentifier, IsFatalError}, + sp_runtime::{traits::Header, DigestItem, DispatchResult, RuntimeString}, + tp_author_noting_inherent::INHERENT_IDENTIFIER, + tp_traits::{AuthorNotingHook, GetContainerChainAuthor, GetCurrentContainerChains}, +}; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; +pub mod weights; +pub use weights::WeightInfo; + +#[cfg(any(test, feature = "runtime-benchmarks"))] +mod benchmarks; +#[cfg(feature = "runtime-benchmarks")] +mod mock_proof; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + type ContainerChains: GetCurrentContainerChains; + + type SelfParaId: Get; + type SlotBeacon: SlotBeacon; + + type ContainerChainAuthor: GetContainerChainAuthor; + + type RelayChainStateProvider: cumulus_pallet_parachain_system::RelaychainStateProvider; + + /// An entry-point for higher-level logic to react to containers chains authoring. + /// + /// Typically, this can be a hook to reward block authors. + type AuthorNotingHook: AuthorNotingHook; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + /// The new value for a configuration parameter is invalid. + FailedReading, + FailedDecodingHeader, + AuraDigestFirstItem, + AsPreRuntimeError, + NonDecodableSlot, + AuthorNotFound, + NonAuraDigest, + } + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: BlockNumberFor) -> Weight { + let mut weight = Weight::zero(); + + // We clear this storage item to make sure its always included + DidSetContainerAuthorData::::kill(); + + weight += T::DbWeight::get().writes(1); + + // The read onfinalizes + weight += T::DbWeight::get().reads(1); + + weight + } + + fn on_finalize(_: BlockNumberFor) { + assert!( + >::exists(), + "Container chain author data needs to be present in every block!" + ); + } + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight((T::WeightInfo::set_latest_author_data(::MaxContainerChains::get()), DispatchClass::Mandatory))] + pub fn set_latest_author_data( + origin: OriginFor, + data: tp_author_noting_inherent::OwnParachainInherentData, + ) -> DispatchResultWithPostInfo { + ensure_none(origin)?; + + assert!( + !>::exists(), + "DidSetContainerAuthorData must be updated only once in a block", + ); + + let registered_para_ids = T::ContainerChains::current_container_chains(); + let mut total_weight = + T::WeightInfo::set_latest_author_data(registered_para_ids.len() as u32); + + // We do this first to make sure we don't do 2 reads (parachains and relay state) + // when we have no containers registered + // Essentially one can pass an empty proof if no container-chains are registered + if !registered_para_ids.is_empty() { + let tp_author_noting_inherent::OwnParachainInherentData { + relay_storage_proof, + } = data; + + let relay_chain_state = T::RelayChainStateProvider::current_relay_chain_state(); + let relay_storage_root = relay_chain_state.state_root; + let relay_storage_rooted_proof = + GenericStateProof::new(relay_storage_root, relay_storage_proof) + .expect("Invalid relay chain state proof"); + let parent_tanssi_slot = u64::from(T::SlotBeacon::slot()).into(); + + // TODO: we should probably fetch all authors-containers first + // then pass the vector to the hook, this would allow for a better estimation + for para_id in registered_para_ids { + match Self::fetch_block_info_from_proof( + &relay_storage_rooted_proof, + para_id, + parent_tanssi_slot, + ) { + Ok(block_info) => { + LatestAuthor::::mutate( + para_id, + |maybe_old_block_info: &mut Option>| { + if let Some(ref mut old_block_info) = maybe_old_block_info { + if block_info.block_number > old_block_info.block_number { + // We only reward author if the block increases + total_weight = total_weight.saturating_add( + T::AuthorNotingHook::on_container_author_noted( + &block_info.author, + block_info.block_number, + para_id, + ), + ); + let _ = core::mem::replace(old_block_info, block_info); + } + } else { + // If there is no previous block, we should reward the author of the first block + total_weight = total_weight.saturating_add( + T::AuthorNotingHook::on_container_author_noted( + &block_info.author, + block_info.block_number, + para_id, + ), + ); + let _ = core::mem::replace( + maybe_old_block_info, + Some(block_info), + ); + } + }, + ); + } + Err(e) => log::warn!( + "Author-noting error {:?} found in para {:?}", + e, + u32::from(para_id) + ), + } + } + } + + // We correctly set the data + DidSetContainerAuthorData::::put(true); + + Ok(PostDispatchInfo { + actual_weight: Some(total_weight), + pays_fee: Pays::No, + }) + } + + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::set_author())] + pub fn set_author( + origin: OriginFor, + para_id: ParaId, + block_number: BlockNumber, + author: T::AccountId, + latest_slot_number: Slot, + ) -> DispatchResult { + ensure_root(origin)?; + LatestAuthor::::insert( + para_id, + ContainerChainBlockInfo { + block_number, + author: author.clone(), + latest_slot_number, + }, + ); + Self::deposit_event(Event::LatestAuthorChanged { + para_id, + block_number, + new_author: author, + latest_slot_number, + }); + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::kill_author_data())] + pub fn kill_author_data(origin: OriginFor, para_id: ParaId) -> DispatchResult { + ensure_root(origin)?; + LatestAuthor::::remove(para_id); + Self::deposit_event(Event::RemovedAuthorData { para_id }); + Ok(()) + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Latest author changed + LatestAuthorChanged { + para_id: ParaId, + block_number: BlockNumber, + new_author: T::AccountId, + latest_slot_number: Slot, + }, + /// Removed author data + RemovedAuthorData { para_id: ParaId }, + } + + #[pallet::storage] + #[pallet::getter(fn latest_author)] + pub(super) type LatestAuthor = + StorageMap<_, Blake2_128Concat, ParaId, ContainerChainBlockInfo, OptionQuery>; + + /// Information extracted from the latest container chain header + #[derive( + Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen, + )] + #[scale_info(skip_type_params(T))] + pub struct ContainerChainBlockInfo { + pub block_number: BlockNumber, + pub author: T::AccountId, + pub latest_slot_number: Slot, + } + + /// Was the containerAuthorData set? + #[pallet::storage] + pub(super) type DidSetContainerAuthorData = StorageValue<_, bool, ValueQuery>; + + #[pallet::inherent] + impl ProvideInherent for Pallet { + type Call = Call; + type Error = InherentError; + // TODO, what should we put here + const INHERENT_IDENTIFIER: InherentIdentifier = + tp_author_noting_inherent::INHERENT_IDENTIFIER; + + fn is_inherent_required(_: &InherentData) -> Result, Self::Error> { + // Return Ok(Some(_)) unconditionally because this inherent is required in every block + Ok(Some(InherentError::Other( + sp_runtime::RuntimeString::Borrowed("Pallet Author Noting Inherent required"), + ))) + } + + fn create_inherent(data: &InherentData) -> Option { + let data: tp_author_noting_inherent::OwnParachainInherentData = data + .get_data(&INHERENT_IDENTIFIER) + .ok() + .flatten() + .expect("there is not data to be posted; qed"); + + Some(Call::set_latest_author_data { data }) + } + + fn is_inherent(call: &Self::Call) -> bool { + matches!(call, Call::set_latest_author_data { .. }) + } + } +} + +impl Pallet { + /// Fetch author and block number from a proof of header + fn fetch_block_info_from_proof( + relay_state_proof: &GenericStateProof, + para_id: ParaId, + tanssi_slot: Slot, + ) -> Result, Error> { + let bytes = para_id.twox_64_concat(); + // CONCAT + let key = [PARAS_HEADS_INDEX, bytes.as_slice()].concat(); + // We might encounter empty vecs + // We only note if we can decode + // In this process several errors can occur, but we will only log if such errors happen + // We first take the HeadData + // If the readError was that the key was not provided (identified by the Proof error), + // then panic + let head_data = relay_state_proof + .read_entry::(key.as_slice(), None) + .map_err(|e| match e { + ReadEntryErr::Proof => panic!("Invalid proof provided for para head key"), + _ => Error::::FailedReading, + })?; + + // We later take the Header decoded + let author_header = sp_runtime::generic::Header::::decode( + &mut head_data.0.as_slice(), + ) + .map_err(|_| Error::::FailedDecodingHeader)?; + + // Return author from first aura log. + // If there are no aura logs, it iterates over all the logs, then returns the error from the first element. + // This is because it is hard to return a `Vec>`. + let mut first_error = None; + for aura_digest in author_header.digest().logs() { + match Self::author_from_log(aura_digest, para_id, &author_header, tanssi_slot) { + Ok(x) => return Ok(x), + Err(e) => { + if first_error.is_none() { + first_error = Some(e); + } + } + } + } + + Err(first_error.unwrap_or(Error::::AuraDigestFirstItem)) + } + + /// Get block author from aura digest + fn author_from_log( + aura_digest: &DigestItem, + para_id: ParaId, + author_header: &sp_runtime::generic::Header, + tanssi_slot: Slot, + ) -> Result, Error> { + // We decode the digest as pre-runtime digest + let (id, mut data) = aura_digest + .as_pre_runtime() + .ok_or(Error::::AsPreRuntimeError)?; + + // Match against the Aura digest + if id == AURA_ENGINE_ID { + // DecodeSlot + let slot = InherentType::decode(&mut data).map_err(|_| Error::::NonDecodableSlot)?; + + // Fetch Author + let author = T::ContainerChainAuthor::author_for_slot(slot, para_id) + .ok_or(Error::::AuthorNotFound)?; + + Ok(ContainerChainBlockInfo { + block_number: author_header.number, + author, + // We store the slot number of the current tanssi block to have a time-based notion + // of when the last block of a container chain was included. + // Note that this is not the slot of the container chain block, and it does not + // indicate when that block was created, but when it was included in tanssi. + latest_slot_number: tanssi_slot, + }) + } else { + Err(Error::::NonAuraDigest) + } + } +} + +#[derive(Encode)] +#[cfg_attr(feature = "std", derive(Debug, Decode))] +pub enum InherentError { + Other(RuntimeString), +} + +impl IsFatalError for InherentError { + fn is_fatal_error(&self) -> bool { + match *self { + InherentError::Other(_) => true, + } + } +} + +impl InherentError { + /// Try to create an instance ouf of the given identifier and data. + #[cfg(feature = "std")] + pub fn try_from(id: &InherentIdentifier, data: &[u8]) -> Option { + if id == &INHERENT_IDENTIFIER { + ::decode(&mut &data[..]).ok() + } else { + None + } + } +} diff --git a/pallets/author-noting/src/mock.rs b/pallets/author-noting/src/mock.rs new file mode 100644 index 0000000..1f19a18 --- /dev/null +++ b/pallets/author-noting/src/mock.rs @@ -0,0 +1,378 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{self as author_noting_pallet, Config}, + bounded_collections::bounded_vec, + cumulus_pallet_parachain_system::{RelayChainState, RelaychainStateProvider}, + cumulus_primitives_core::ParaId, + frame_support::{ + inherent::{InherentData, ProvideInherent}, + parameter_types, + traits::{ + ConstU32, ConstU64, Everything, OnFinalize, OnInitialize, UnfilteredDispatchable, + }, + }, + frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}, + parity_scale_codec::{Decode, Encode}, + polkadot_parachain_primitives::primitives::RelayChainBlockNumber, + polkadot_primitives::Slot, + sp_core::H256, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BoundedVec, BuildStorage, + }, + sp_state_machine::StorageProof, + test_relay_sproof_builder::ParaHeaderSproofBuilder, +}; + +type Block = frame_system::mocking::MockBlock; +type AccountId = u64; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + AuthorNoting: author_noting_pallet::{Pallet, Call, Storage, Event}, + MockData: mock_data, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; + type RuntimeTask = (); +} + +parameter_types! { + pub const ParachainId: ParaId = ParaId::new(200); +} + +// Pallet to provide some mock data, used to test +#[frame_support::pallet] +pub mod mock_data { + use {super::*, frame_support::pallet_prelude::*}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn mock)] + pub(super) type Mock = StorageValue<_, Mocks, ValueQuery>; + + impl Pallet { + pub fn get() -> Mocks { + Mock::::get() + } + pub fn mutate(f: F) -> R + where + F: FnOnce(&mut Mocks) -> R, + { + Mock::::mutate(f) + } + } +} + +impl mock_data::Config for Test {} + +#[derive(Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Mocks { + pub container_chains: BoundedVec>, +} + +impl Default for Mocks { + fn default() -> Self { + Self { + container_chains: bounded_vec![1001.into()], + } + } +} + +pub struct MockAuthorFetcher; + +impl tp_traits::GetContainerChainAuthor for MockAuthorFetcher { + fn author_for_slot(slot: Slot, _para_id: ParaId) -> Option { + Some(slot.into()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_authors_for_para_id(_para_id: ParaId, _authors: Vec) {} +} + +pub struct DummyBeacon {} +impl nimbus_primitives::SlotBeacon for DummyBeacon { + fn slot() -> u32 { + let block_number = System::block_number(); + + block_number as u32 + } +} + +pub struct MockContainerChainGetter; + +impl tp_traits::GetCurrentContainerChains for MockContainerChainGetter { + type MaxContainerChains = ConstU32<100>; + + fn current_container_chains() -> BoundedVec { + MockData::mock().container_chains + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_current_container_chains(container_chains: &[ParaId]) { + MockData::mutate(|m| { + m.container_chains = container_chains.to_vec().try_into().unwrap(); + }); + } +} + +pub(crate) const MOCK_RELAY_ROOT_KEY: &[u8] = b"MOCK_RELAY_ROOT_KEY"; + +pub struct MockRelayStateProvider; + +impl RelaychainStateProvider for MockRelayStateProvider { + fn current_relay_chain_state() -> RelayChainState { + let root = frame_support::storage::unhashed::get(MOCK_RELAY_ROOT_KEY) + .expect("root should be set by mock"); + + RelayChainState { + state_root: root, + number: 0, // block number is not relevant here + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_current_relay_chain_state(state: RelayChainState) { + frame_support::storage::unhashed::put(b"MOCK_RELAY_ROOT_KEY", &state.state_root); + } +} + +impl Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type ContainerChainAuthor = MockAuthorFetcher; + type SelfParaId = ParachainId; + type SlotBeacon = DummyBeacon; + type ContainerChains = MockContainerChainGetter; + type AuthorNotingHook = (); + type RelayChainStateProvider = MockRelayStateProvider; +} + +struct BlockTest { + n: BlockNumberFor, + within_block: Box, + after_block: Option>, +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} + +fn wasm_ext() -> sp_io::TestExternalities { + new_test_ext() +} + +/// BlockTests exist to test blocks with some setup: we have to assume that +/// `validate_block` will mutate and check storage in certain predictable +/// ways, for example, and we want to always ensure that tests are executed +/// in the context of some particular block number. +#[derive(Default)] +pub struct BlockTests { + tests: Vec, + ran: bool, + relay_sproof_builder_hook: + Option>, + inherent_data_hook: Option< + Box< + dyn Fn( + &BlockTests, + RelayChainBlockNumber, + &mut tp_author_noting_inherent::OwnParachainInherentData, + ), + >, + >, + overriden_state_root: Option, + overriden_state_proof: Option, + skip_inherent_insertion: bool, + skip_author_noting_on_initialize: bool, +} + +impl BlockTests { + pub fn new() -> BlockTests { + Default::default() + } + + fn add_raw(mut self, test: BlockTest) -> Self { + self.tests.push(test); + self + } + + pub fn add(self, n: BlockNumberFor, within_block: F) -> Self + where + F: 'static + Fn(), + { + self.add_raw(BlockTest { + n, + within_block: Box::new(within_block), + after_block: None, + }) + } + + pub fn with_relay_sproof_builder(mut self, f: F) -> Self + where + F: 'static + Fn(&BlockTests, RelayChainBlockNumber, &mut ParaHeaderSproofBuilder), + { + self.relay_sproof_builder_hook = Some(Box::new(f)); + self + } + + pub fn with_overriden_state_root(mut self, root: H256) -> Self { + self.overriden_state_root = Some(root); + self + } + + pub fn with_overriden_state_proof(mut self, proof: StorageProof) -> Self { + self.overriden_state_proof = Some(proof); + self + } + + pub fn skip_inherent_insertion(mut self) -> Self { + self.skip_inherent_insertion = true; + self + } + + pub fn skip_author_noting_on_initialize(mut self) -> Self { + self.skip_author_noting_on_initialize = true; + self + } + + pub fn run(&mut self) { + self.ran = true; + wasm_ext().execute_with(|| { + for BlockTest { + n, + within_block, + after_block, + } in self.tests.iter() + { + // begin initialization + System::reset_events(); + System::initialize(n, &Default::default(), &Default::default()); + + // now mess with the storage the way validate_block does + let mut sproof_builder = ParaHeaderSproofBuilder::default(); + if let Some(ref hook) = self.relay_sproof_builder_hook { + hook(self, *n as RelayChainBlockNumber, &mut sproof_builder); + } + + let (mut relay_storage_root, mut relay_storage_proof) = + sproof_builder.into_state_root_and_proof(); + + if let Some(root) = self.overriden_state_root { + relay_storage_root = root; + } + + if let Some(state) = &self.overriden_state_proof { + relay_storage_proof = state.clone(); + } + + // We write relay storage root in mock storage. + frame_support::storage::unhashed::put(MOCK_RELAY_ROOT_KEY, &relay_storage_root); + + // It is insufficient to push the author function params + // to storage; they must also be included in the inherent data. + let inherent_data = { + let mut inherent_data = InherentData::default(); + let mut system_inherent_data = + tp_author_noting_inherent::OwnParachainInherentData { + relay_storage_proof, + }; + if let Some(ref hook) = self.inherent_data_hook { + hook(self, *n as RelayChainBlockNumber, &mut system_inherent_data); + } + inherent_data + .put_data( + tp_author_noting_inherent::INHERENT_IDENTIFIER, + &system_inherent_data, + ) + .expect("failed to put VFP inherent"); + inherent_data + }; + + // execute the block + if !self.skip_author_noting_on_initialize { + AuthorNoting::on_initialize(*n); + } + + if !self.skip_inherent_insertion { + AuthorNoting::create_inherent(&inherent_data) + .expect("got an inherent") + .dispatch_bypass_filter(RawOrigin::None.into()) + .expect("dispatch succeeded"); + } + within_block(); + AuthorNoting::on_finalize(*n); + + // clean up + System::finalize(); + if let Some(after_block) = after_block { + after_block(); + } + } + }); + } +} + +impl Drop for BlockTests { + fn drop(&mut self) { + if !self.ran { + self.run(); + } + } +} diff --git a/pallets/author-noting/src/mock_proof.rs b/pallets/author-noting/src/mock_proof.rs new file mode 100644 index 0000000..463e1c6 --- /dev/null +++ b/pallets/author-noting/src/mock_proof.rs @@ -0,0 +1,7139 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +pub const ENCODED_PROOFS: &[(u32, &str, &[&str])] = &[ +(0, "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", &[ +]), +(1, "b2ab0e893a631ef1bb33b2429566b9352dc55ea24d1f94bf3c5c4b58db017c8b", &[ +"3f39cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3b4def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(2, "02ec33a53ca5079845bbcfb7bf417b3e4a74ab5b4243d788b963dcb849f7e1ac", &[ +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3704def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32008805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580b8fbd180402857391788fd58d3b31c8a6e6acf38c966f04d2d21aac579f0f219", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(3, "87982082cb1c76986cc2a1b26af250763e9152f3d97b072df91c5c99a624c665", &[ +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3704def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3200a805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380b8fbd180402857391788fd58d3b31c8a6e6acf38c966f04d2d21aac579f0f219", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(4, "aeb02daf64d090ea9354996856d95d2d01905597d7fd6fee73a9527cc157c0e3", &[ +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80108080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef654801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3200a805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380b3a05087c6d3b7c815bbe8865c4198226875db043093184ea55d232572864c59", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(5, "6e968f98e0c49d3287f19df1405f42a7f37ea858b22a1f573391bd3c28176c0d", &[ +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80108080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef654801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3202a805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380b3a05087c6d3b7c815bbe8865c4198226875db043093184ea55d232572864c59808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(6, "8a3881730253198f3518a55fb1571554cf6f28783f084b29ce918391152ffc33", &[ +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80108080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef654801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3282a80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c5805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380b3a05087c6d3b7c815bbe8865c4198226875db043093184ea55d232572864c59808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(7, "542952c121d8121bc3c92797468047c873ac0472c591eb0320d06302c9c43c7e", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80508080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da304801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3282a80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c5805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380150871c26641b8a18bcd06619ccb4a1b1a6fcb8b4514ccb946d722a81035de70808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(8, "358d100f08be73c92e0cbef1112f9fc8b3964555e346b1166e1412da762bead8", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80508080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da304801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3292a80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c5805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380150871c26641b8a18bcd06619ccb4a1b1a6fcb8b4514ccb946d722a81035de70808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(9, "932ba2bfca901da99cf913a1a1251c1574772fca247044fca9de2bcc7b1321c3", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370153cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80508080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da304801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3392a80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c58021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e805daf3d34595790f5e0b7fd3f8a1602e3037fe9c01fecaf97b9f2c3a47bdd6cf580d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380150871c26641b8a18bcd06619ccb4a1b1a6fcb8b4514ccb946d722a81035de70808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(10, "0a1dfdf13f32c6226bfaef50281786c793d92cd4757f10e616bea4cfd7b7a519", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80508080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da304801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3392a80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c58021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d080d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b380150871c26641b8a18bcd06619ccb4a1b1a6fcb8b4514ccb946d722a81035de70808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(11, "b5a1185bb1d7e3729761bbfb16b5c483dd6e55f2f073870e2623a369923a0e34", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80508080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da304801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3392e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c58021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d080d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd2280150871c26641b8a18bcd06619ccb4a1b1a6fcb8b4514ccb946d722a81035de70808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(12, "8c8c8671c3d01590d4676073c851584fa2fd9eb2ed3f1dc0c8ffc84a507e6143", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3392e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c58021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d080d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca89808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(13, "38fde536e1453db0c4d82529f6a5ccc448218a5adec53a669af3c0e7e88d9e88", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3396e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80c600c207d01c2c289813b51608e03c0ee423351c7ef4e8e30075d026ee3485c58021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d080d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca89808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(14, "df839fd538e98a9cc2921a5413e0be0c2c631060cf74fd77ae65a323b61206b7", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3396e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d080d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca89808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(15, "d77d0abe90c65dca0e62a771a1f6f1c451e60cd2ce57e7344ffdfd1fe9b69e87", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370b25ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3796e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d080017fcf7ea721db85622f499809b33f2d9c1538cbfb264b7e7d5cebdbf007099780d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca89808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(16, "953508b0a6469355a675c4030ed42902d4a8de324b040449bb839a2436f5a4c7", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370b25ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370bed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3f96e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d080017fcf7ea721db85622f499809b33f2d9c1538cbfb264b7e7d5cebdbf007099780206445d23bd6909453e061dbebc62741ae2c9904f8f2b7635e76c9239a7fcf1280d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca89808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(17, "b50e914243762d5d52a23519484e63bc262397fc8f6717d74f0c5fc7470ebc45", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370bed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3f96e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d0805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80206445d23bd6909453e061dbebc62741ae2c9904f8f2b7635e76c9239a7fcf1280d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca89808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(18, "826bc6051e895d1d67ad242a7d66409e25c80c7b11c0bdb2f27fcb6964103d69", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3702d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370bed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3f97e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d0805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80206445d23bd6909453e061dbebc62741ae2c9904f8f2b7635e76c9239a7fcf1280d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980ad550109429b70b894031fe36b782d487be0e89ce368c68cd4ef904057a2f5fa808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(19, "4213c5836b1a371216b7a59725f77d701b0415da3c6410c8216576f6b21d5a3c", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370bed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3f97e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e808adb7128b638d6c55354ba0a2edcbdc73754524dd3e85dd41731be76894b54d0805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80206445d23bd6909453e061dbebc62741ae2c9904f8f2b7635e76c9239a7fcf1280d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(20, "5026fa3d653b3b5982c681ae24c229421502bbd0fccbc5a7c436c747eed02d73", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370bed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3f97e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e80b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80206445d23bd6909453e061dbebc62741ae2c9904f8f2b7635e76c9239a7fcf1280d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(21, "b8fa8dc00483122f9b64ee0016493824c9eeea363c74c78e7bbb27eed6e74b9e", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370bed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f3580b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80206445d23bd6909453e061dbebc62741ae2c9904f8f2b7635e76c9239a7fcf1280d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(22, "cf89679cc278b481f61322b0780669d361d2cb5d7c439e3d906a3b0a37bf54a2", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370eb2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f3580b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c080d5f6cd2c30d25655c3b347412b49761830f83702a9705c956b6683b66611e2b38007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(23, "5dee3a050f013c35671b21f81e16258bece9d356ddcfbb9223b12c430169a4cd", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3709c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800440804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e08086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f3580b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c08058a70212812acb1ca24042410ec4808a4082d3cca3f4eb87639aad3c6f3ec6bf8007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808f4fb6d62059d528a066144dc6742ce52aef0d20a0863d218a3e331ddbfd1970801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(24, "ec79c439c702690be363ea0c9fe45fd540d73cdcb15e39522b798059d33a8167", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800440804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e08086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7e80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f3580b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c08058a70212812acb1ca24042410ec4808a4082d3cca3f4eb87639aad3c6f3ec6bf8007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(25, "ee1c9a6c6a6601cc9dea297437ce36e517846f2847ddc9ac8e2728fa5cf270e1", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f8763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800440804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e08086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f3580b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c0806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e8058a70212812acb1ca24042410ec4808a4082d3cca3f4eb87639aad3c6f3ec6bf8007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec801bd030c6922eb0315ba99ebf554e9782e828edac27dae3092036e6cd9f8f4979", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(26, "52e02dc01145842b0ed1c5c4a1bc3d8b3461d2be067efe78e56fd3760295cd90", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80020280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f97230680e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800440804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e08086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f3580b0a8a0fb4fc4c98430b66e832a8d053111a7df44a6217ecac95f31f8d608f4688021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c0806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e8058a70212812acb1ca24042410ec4808a4082d3cca3f4eb87639aad3c6f3ec6bf8007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(27, "fd7e2c543edb7a8aae96870440255ec760059cd7a1a904c36006e3f860b6d048", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3706b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800440804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e08086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c0806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e8058a70212812acb1ca24042410ec4808a4082d3cca3f4eb87639aad3c6f3ec6bf8007c699bfd292627df4a3a9e9b1d83a3a1fa2b1d3ab08132536dec3943d9ccd22809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(28, "20c073de1ad929a4813138f4f39a8ced7f250dd9e809277c336005da2a742bb4", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800440804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e08086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8050c080cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c0806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e8058a70212812acb1ca24042410ec4808a4082d3cca3f4eb87639aad3c6f3ec6bf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac809e876d296189987bb0189362197bc587d90a6c4e497208b646de659c77e0ca8980688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(29, "cba2b43efb60ccb739003a6d074018562daa9e2ba87e927be0eeba178fc18cb9", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800440804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e08086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c0806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e8058a70212812acb1ca24042410ec4808a4082d3cca3f4eb87639aad3c6f3ec6bf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(30, "af864c51d89b8b67b8d57da6e3ef3b285a805fed5a9696bacd42049b35b15a08", &[ +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370e0d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80d295d1e98eec95623add624c2f88424542f277e74ed041f2b07ba1ba52d1527e806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c0806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(31, "9bb58142083046de2f0ecb495ef913a3f39519fd1e7e43a93c913a8a996102d1", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"80100880258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b803c3129359cad79a2195f5312223fd224c6c5e723801dc4c2ab480bf231baa4c0806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(32, "bc7f9cc69315247ce83daa9da458c8c56b162601670a26f737e5b8b73e08f4a1", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2808e5bbef4cac7b9f03b34ca5c4cbfb7963d94709077b92d5031e4a43f1a709cec8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(33, "7c3f6ae66198d97ad67943a181d334f7900a383253c317ae0be4b72d65333566", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008828087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8021f86ed0abca5a9958c325eb63c5309aa820d4ff05441e60998c375079ab315e8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f280c0b85a615597d3e1d9adea44d53b875101bd46082fe8aba857907ac26b4d0df98010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(34, "5f2f581543f6bae7f1ee4b7615827268111f45ca4f0fd239d1c175969bc0ef38", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008828087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf66803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f280c0b85a615597d3e1d9adea44d53b875101bd46082fe8aba857907ac26b4d0df98010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(35, "00348281dfdb0d87431010c4e9972367658c7db783c74080c91ff92753b1ed4a", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370f0e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff806c471a5a533bad5bfe6f436e01479b8a3ffa6cc349e39a5e5523fdb63a800f35803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(36, "bd55e82aef5be71ecf57b23084d1253b7a5185e3c9b6dfb9b34f7e8f8a34b63d", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"8080808011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fb7f80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff807a56ce2d7712898c0d7cf5770cbf5c6d13364c9c5974a4a41f8fc61499be4f61803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae2", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(37, "653c040ed56010c7329484a57b8a182229cc1908331d5d9d4efb90860a3df023", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3701fc507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"8080808011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fbff80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff807a56ce2d7712898c0d7cf5770cbf5c6d13364c9c5974a4a41f8fc61499be4f61803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28072e030ca172d3a1e02d3e9f2c0b88a21bab058c593f9460e9b9ee520db0b8407", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(38, "9f968f345d7c5604749c50c40be56d5fe139d025423d71f5224d1f4c1ee13b12", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3701fc507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3fbff80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f27803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28072e030ca172d3a1e02d3e9f2c0b88a21bab058c593f9460e9b9ee520db0b8407", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(39, "514ff098f8be6d5194240197e3f011b74ed4e4a6a8d02fd3e972fea0ae9a0d63", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3701fc507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"801202809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a0803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da8050594cc778b2b84eb2937ace0b735780d0e594d74bc9779c04672142d0ff10d1805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28072e030ca172d3a1e02d3e9f2c0b88a21bab058c593f9460e9b9ee520db0b8407", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(40, "4a26e56138abb01608a9807c20fd1964b2a466cb2c40ab05478fabda9a67b7b2", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3701fc507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80024080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80130280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80a1c82554a4a12a0706699b8360e814c7bd0b433a50f001cecadf7d64cc45b7ff80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a0803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da804843dbb1f9e9ceb4c9214f2cfd7c71834c3918aa1355125e5f515444f6205673805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28072e030ca172d3a1e02d3e9f2c0b88a21bab058c593f9460e9b9ee520db0b8407", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(41, "eff1078ea5e9ea92497ad8b097b56927bc0305122e60263d9bed68e191f31f70", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3701fc507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80120280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80130280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a0803a35efd3ad925d2d0497021c41eeb4f88f077a44b502c4a1ef62880057d52c2c8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da804843dbb1f9e9ceb4c9214f2cfd7c71834c3918aa1355125e5f515444f6205673805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28072e030ca172d3a1e02d3e9f2c0b88a21bab058c593f9460e9b9ee520db0b8407", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(42, "741c4d4a2c6c14a855832a3f322793312c4a8cd9ca6ba4bfdb93af7092f3a5de", &[ +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3701fc507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80121280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec3", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80130280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08017408758681ab8cdaf0f3abfa3ca64d298b4a512e7dda5c9cc8e87811a43a2fe8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da804843dbb1f9e9ceb4c9214f2cfd7c71834c3918aa1355125e5f515444f6205673805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28072e030ca172d3a1e02d3e9f2c0b88a21bab058c593f9460e9b9ee520db0b8407", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(43, "b5144749d474539a02e477fdf794d21a15006f355a894f48f1f9fc141e67a6c6", &[ +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80121280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec3", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80130280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c808c21b9ea676d83a7685ae64ee202342e9ef12962b21fd83306f39346ffab3588", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"81010180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08017408758681ab8cdaf0f3abfa3ca64d298b4a512e7dda5c9cc8e87811a43a2fe8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da804843dbb1f9e9ceb4c9214f2cfd7c71834c3918aa1355125e5f515444f6205673805ee92edf77b5ce7dd582d0142093cb93edc6816360abc4b90cb485040c6f1e8b80ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28046c72f7a71d8ef6af974a8aa9f821238d4314dad4bb8823cc28c6d4b41d98180", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(44, "7f867f8d48a2f2a2ec532722af96739b756aa0a8f271af040336775543f4b066", &[ +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80121280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec3", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80130280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"81010180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08017408758681ab8cdaf0f3abfa3ca64d298b4a512e7dda5c9cc8e87811a43a2fe8064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da804843dbb1f9e9ceb4c9214f2cfd7c71834c3918aa1355125e5f515444f62056738089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28046c72f7a71d8ef6af974a8aa9f821238d4314dad4bb8823cc28c6d4b41d98180", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(45, "f2bf33d541038b7b55cc99d32024411bea353a87d0ac04ed4a17529ad7f26f41", &[ +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80130280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"81010180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da804843dbb1f9e9ceb4c9214f2cfd7c71834c3918aa1355125e5f515444f62056738089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28046c72f7a71d8ef6af974a8aa9f821238d4314dad4bb8823cc28c6d4b41d98180", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(46, "8c54785d8170059e65fa0dec459a9f7280529c496dcea8f6703e8a3be0eab26f", &[ +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"81010180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28046c72f7a71d8ef6af974a8aa9f821238d4314dad4bb8823cc28c6d4b41d98180", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(47, "811fec3246b2e88193534771bac5eb924b928e7e09c888332f959428ac90ed62", &[ +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e8019c801283c4dcb7f5215285a810ccb938b8af7a8fc3eedb5203d45d0a46a69d1", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80140880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d46", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780ec5cc08eb0cfa9c33038d4ed85b38d0e4e23e0bfe7c7d925b6e84d5cd90b66ab806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28055249cd029dbb71406e441d9b83fa57f884b6db86b1e48e7e31248b3b5b121dc", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(48, "3e596a4f3dc22a05d5f2b468ae20054f739dbad74f4d9725c164f76e0258da84", &[ +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e8019c801283c4dcb7f5215285a810ccb938b8af7a8fc3eedb5203d45d0a46a69d1", +"80124080e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d580af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8025fb7ddfbe9d4a74b9f29ee802f160f51cfb47abc0266989be8447a4b9963d2780dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28055249cd029dbb71406e441d9b83fa57f884b6db86b1e48e7e31248b3b5b121dc", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(49, "2da2007b7410ec5b794f792d7512a0313365393664567b93d3b22f3e38c7211f", &[ +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3707351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e8019c801283c4dcb7f5215285a810ccb938b8af7a8fc3eedb5203d45d0a46a69d1", +"80124280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8046e39c9a8b7829759c026c054489da511e9cf23d579367beda812bfdc1d11d7c80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d806816a6558cf8ed512b3fdb0294aad5405c2ab1e6c57532e8bd88d61446f24e9e80c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28055249cd029dbb71406e441d9b83fa57f884b6db86b1e48e7e31248b3b5b121dc", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(50, "08ba6e5b104aeff46d7558721c5ffb2d307a904258d3e6b757aa233af605202e", &[ +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e8019c801283c4dcb7f5215285a810ccb938b8af7a8fc3eedb5203d45d0a46a69d1", +"80124280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8046e39c9a8b7829759c026c054489da511e9cf23d579367beda812bfdc1d11d7c80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec6167180c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae28055249cd029dbb71406e441d9b83fa57f884b6db86b1e48e7e31248b3b5b121dc", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(51, "e39c485c0b448759b69719298bb3fca5098fa656cf4657c8fdb219d453449673", &[ +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366f3965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"800460804bda4345a327c69cfb1a460b496f4308547c5b6dff5f45544813c7f296cdb7e080b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80124280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8046e39c9a8b7829759c026c054489da511e9cf23d579367beda812bfdc1d11d7c80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec6167180c4197388f534b001e615eec4f737f7fa7cf8ff5771d4be33285a7023fa8f78f380676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(52, "3213f300fb439adf0c8fd6c61636f22d34cf418e650ef495e2f78132f0b514e5", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80124280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"808080806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8046e39c9a8b7829759c026c054489da511e9cf23d579367beda812bfdc1d11d7c80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58064497ded3218d1ba2f82551bcac3abf617a2c725dd54b992b6339ff16dc525da802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(53, "a199ddb5c669e2b024b4969f2136d4aa002bca0a305ed29ca6d73a73249758da", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80124280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"804008801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c80ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8046e39c9a8b7829759c026c054489da511e9cf23d579367beda812bfdc1d11d7c80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58083a2d31543870d9943cc15f053fd9db8b6a7c036e5cdcb37a49051c7821b90c9802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8089f0295eadf3a847f5bd32c1365ca4c400b424bb32005f709954e2ad96d6add780628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(54, "1d4fe71e6036726528ac6929941cece3950c7c8f92e36a0c9709cd1c50408bd7", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80124280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff8046e39c9a8b7829759c026c054489da511e9cf23d579367beda812bfdc1d11d7c80dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58083a2d31543870d9943cc15f053fd9db8b6a7c036e5cdcb37a49051c7821b90c9802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(55, "c276f168d8160a785e061b16653cf0c02fa62d183aa019b2b9bf6089b58e1d17", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ab1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2806c0029cd50ed80f251d175a8342dd45078c031358b25f544850431da1ae573538019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f58083a2d31543870d9943cc15f053fd9db8b6a7c036e5cdcb37a49051c7821b90c9802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(56, "cfd592b79031884d80dc05639ed611a9d6ea3f7337c37522ebfc70c1abd27a44", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800044803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a09809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004018064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f8", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef280024a49ff0b5f3eefdaf569de9eacb3eff9fc56570df1267be7382c0a5f86c3ad8019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f5806524ad3ce2bc20cdf490d6819ab69e2d38d4538642a0f904c463132468d62def802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80688d4df901a64929ce89cb558c000839cbf9e26ea30ba7f5451ee5f798ea47f2800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(57, "4f218eced95492bc82fe95534fbcef306b0e471c410b1bb2b3576fe9392bca2a", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800044803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a09809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125280ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b9080e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef280024a49ff0b5f3eefdaf569de9eacb3eff9fc56570df1267be7382c0a5f86c3ad8019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080eea952c3f34bf874953c7e39208ba3c42377bd50ed9fad16843fd456679229f5806524ad3ce2bc20cdf490d6819ab69e2d38d4538642a0f904c463132468d62def802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(58, "67591660c23cfcfd2c1d76e6023780e9fa58de590e76c52d96f89cdeffd70a94", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800044803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a09809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125380ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8052c080e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef280024a49ff0b5f3eefdaf569de9eacb3eff9fc56570df1267be7382c0a5f86c3ad8019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080146ff11f04d93fb61ef5b76bb525acf4a2faf31554a09aa087b12be29be56bd8806524ad3ce2bc20cdf490d6819ab69e2d38d4538642a0f904c463132468d62def802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac80e73d54ab5ee0577d32068ea39913439d0ef01fd21a2d012df52c1e49fb525b2e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(59, "c5b6a99dffcde6ca971b8dc283e9aba51679f149fc6113b3f79c5728700f3c85", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800044803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a09809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125380ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef280024a49ff0b5f3eefdaf569de9eacb3eff9fc56570df1267be7382c0a5f86c3ad8019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080146ff11f04d93fb61ef5b76bb525acf4a2faf31554a09aa087b12be29be56bd8806524ad3ce2bc20cdf490d6819ab69e2d38d4538642a0f904c463132468d62def802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(60, "ed1340c229c96ddb82359882b4108fb1b1187d737091b4f855ef2a67e936ca54", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800a0080a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125380ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080146ff11f04d93fb61ef5b76bb525acf4a2faf31554a09aa087b12be29be56bd88082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280ea8d6faedcf64fa17af64d887eefeda987c8b8ed8baaa3f3029988f52f3aefc1", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(61, "3eb90c410a9d4d76f2325f8346a775b887a74e18233a4fc147a9fd363c5140ca", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125380ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400280f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a2938088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080146ff11f04d93fb61ef5b76bb525acf4a2faf31554a09aa087b12be29be56bd88082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf80676423908b2abb20630e230ebb2ba986e4fe3175d93fd78ff92557b48d9fa4ac808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(62, "849b436dd551563eb33612e4e657ac0d47cac7bbadc0a9555b338436900dfb05", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80125380ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080146ff11f04d93fb61ef5b76bb525acf4a2faf31554a09aa087b12be29be56bd88082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(63, "478aecfc36420f9c9f94459608c1ba39c387ae06946a758a7819a3a58c3d37c5", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3635383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e80ba100d8205178e32867a31e9691e61f2b1be5faa67306e136f2347bb8f9723068063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a0809a5401680df902c050cc77da76247493138973e105f0a738405301eb029527288082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(64, "2a23267b790b6c13eb5c1b960cf9e686b33e74c57a41896403cd2781289f545e", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a4280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80456870037dca8a846f89115705ad5287a1e89224cd619e64f2c46e687879380580dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(65, "84719e1eca09c87ab7c64e9dd4f2d18ab67bc935f43b0ac70ab6904226cdfea5", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"80046080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671805cdcdc506cef4cbde63abdd64d34641863f9f1c2ab878e35d0a22d581d8d25cf802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(66, "e58ba82130be591871ad6b1d81caa44d0821b47b956b9e7237d37a2f05a7714c", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3687dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d80ebc2a061e9dac3dfb463c6dae34575c708e6f40a7499a762839452767a5ba1f680df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680dfa35075ae1cec4413b52f1aebe6d9b2c6d552535f556ea49a71cdee8d2b9f2780aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(67, "5a1eb7ca6c189eef701f38613645b575ea8fe3a988dad1e14b75849617c9dcb5", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400c801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c8021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac8061c8513f8b44db012557b101f6eae8e9834497cc281e9162f22bdafc00074c3080628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(68, "443a300f65ff78ef82d0e1249ff8cc472e3f1996a12b37d8179ad62bacf7fbb8", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004058064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400e801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac80f37e248886162c3a3422f8615b24098a703b60940512e05aed9d957638cf9b3e80628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80c5b74caffec30153ceb77805e2116b3f73939c8aef5c16db327ce3b4360005e8800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(69, "56f71110e35307a12ee10c095e0ee6531f3ab622104c2850bc36626e96c9ced5", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"8008928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400e801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac80f37e248886162c3a3422f8615b24098a703b60940512e05aed9d957638cf9b3e80628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b7800f84e8b76210b123fb2cc384b04d0fa398f22e30ec27c7b1f706cb2a51bb4b5b8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(70, "896af49730cc259282019f44ead09c316f7124df4d53466b4358769f8c71c91b", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80400e801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac80f37e248886162c3a3422f8615b24098a703b60940512e05aed9d957638cf9b3e80628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b780e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(71, "dcab65ad58a02ac87a328a8a19f81b5c05d87d90951f74e0ab46184a6797649d", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cc9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac801d372746b13555996b155819beb8afdbaa115efc81f96b770ad8225ab2445dae80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473802a011526a030d30bf0060887bee1f3bffb6f8044024a6da75307b2e66098c6ac80649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b780e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(72, "cb763437ca50f6f605cf494e6807043183c934e3ba9f86e6e3bb109c569dc901", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3656c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80930280763ff151e9b4657f9990f8a63a883cf18367125f84d4a2a02d10a3f308b0a83d809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473806bfe37e26a937f7fba4589c87b60c5a2e57a9200c6d83b887bdbe7cec5e2e15480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b780e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(73, "b8f9213bdf3f9fd29d0a20bd8d9b6a12b03b330e1d556870bb8320d95aea7161", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80400380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb717", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e802ee0f6f97c2ab1ce5f9c0f5f07d008930ac0f51469c83a6ff8466e60eda2eec7808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b780e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(74, "8b2c1940dbde5bf54b7668b3ba18ae359c256a5d4d13a339967f54bec923f3c0", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c9c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af80745b87b7b7c6e7b44197818ffb1a42d7e3eecb061201a1403b18b195edca5ec380b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a08099ef1ea287af00e3092d14a429b80e8c5823e672177a549c94ccea2de7d158c48082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b780e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(75, "8f188c07bf8e324213fa34ff22ccd1d7904e01081e1528aa6b14040522b4a2df", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af8058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"80818080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b1268019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080b87694ac2a77879caec0c112e89692100b623b1ccb9ac8fcfcb219479ebccb4b8082464d1a7f5a1d35d9f583ad600473bf04e5268a48b68ddcfb5592f3b2e1a473804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b780e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(76, "4dbea5abd79412579a5d943353571f988596b95503e12e48a90e898cbd6701b5", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c39180990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af8058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080b87694ac2a77879caec0c112e89692100b623b1ccb9ac8fcfcb219479ebccb4b80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80dc059529172534e31b8dcf909d2de070fc8fc226b85769b7c4240c1bc38998b780e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(77, "1e5591c62c3e0b91103b918c6f72df26cf3ca24f1a5eb2937cf476c916a5cbce", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af8058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804080402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080b87694ac2a77879caec0c112e89692100b623b1ccb9ac8fcfcb219479ebccb4b80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80587dfee5aac37203c7cd269ad8fe6de582c875a4e88d77b151cc57711ec61671804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80635cd90dc3330d1ea606c733eb1884109d7e33f68c40d01c74a8e47b6fb813b680e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(78, "20197241778e18f7e8328e30f96d061d83daf0cd0a3866ead101122eb55c4bdf", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"370c1954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af8058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e80aa56d24ce9e9a7c5a7b06844abb253ec544ef2244cdc84cf01f72b89fb0ee9a080b87694ac2a77879caec0c112e89692100b623b1ccb9ac8fcfcb219479ebccb4b80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80635cd90dc3330d1ea606c733eb1884109d7e33f68c40d01c74a8e47b6fb813b680e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(79, "7e3f0a75d0746df5b3faa9dcc90e0209c2a50757dd752106bea1206ba7df1546", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af8058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80148880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80b87694ac2a77879caec0c112e89692100b623b1ccb9ac8fcfcb219479ebccb4b80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef880628319e3b9084bd5596a427c00d786e902debd69cd577ac2be083e71b939dd5d80e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80635cd90dc3330d1ea606c733eb1884109d7e33f68c40d01c74a8e47b6fb813b680e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(80, "76cfa2fd4c395aac0f611c92215b18caaa32d24fe455c4ba405e1903efad794e", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af8058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec160803902cf71be2c3bc0a7c9e7da6ba580019bd4802db998fbbf7723c85acc5fbf668021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80b87694ac2a77879caec0c112e89692100b623b1ccb9ac8fcfcb219479ebccb4b80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a9005480e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80635cd90dc3330d1ea606c733eb1884109d7e33f68c40d01c74a8e47b6fb813b680e4fb338f8ff160780e9f806bc567edc3413bc5d370d281d830428f90bf97b4498010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(81, "9135e264dd83dbc8e1a7e6ece2224c4d521628ec4115f6e5a91635b3b4363124", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350adf0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b9d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"800210804e69397633b7cc5f01353feb7a30392e7180fee11bfe0a9d4077d8e92932cbf0803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f9980e2dc3d1fe8cf87075c5f7f1aa4cea634d3cf2d90d5ff95da68c8edbeaaa582af8058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec1608050da9108f67e43f86e887efadf46652106005f82140cfda88093f27a0e67fd6d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80b87694ac2a77879caec0c112e89692100b623b1ccb9ac8fcfcb219479ebccb4b80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a9005480e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80635cd90dc3330d1ea606c733eb1884109d7e33f68c40d01c74a8e47b6fb813b68068be96b3276d6b37b3e075370fd8f677b406403548b5cfb4dad670cba871924f8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(82, "e7f25a8dc72e740791c6e1a990aed37fa36aa2f533dead83ae79aadf140d619f", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350adf0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3670660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"800210804e69397633b7cc5f01353feb7a30392e7180fee11bfe0a9d4077d8e92932cbf0803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80fab4494e214b907b4924c298d4fb3a5c79e1614dca72aed54a0654f9e4172920", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec1608050da9108f67e43f86e887efadf46652106005f82140cfda88093f27a0e67fd6d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80ee91f57386d1aa49c0343574f46fe2e5327b4f5bc181d5209ea876cfff64feac80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a9005480e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e80635cd90dc3330d1ea606c733eb1884109d7e33f68c40d01c74a8e47b6fb813b68068be96b3276d6b37b3e075370fd8f677b406403548b5cfb4dad670cba871924f8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(83, "aef4f10a8fb41f1342540c662f27b27c4ee921427c23bdd7f730bf3c25241c2a", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350adf0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"800210804e69397633b7cc5f01353feb7a30392e7180fee11bfe0a9d4077d8e92932cbf0803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80135380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f97008063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec1608050da9108f67e43f86e887efadf46652106005f82140cfda88093f27a0e67fd6d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80ee91f57386d1aa49c0343574f46fe2e5327b4f5bc181d5209ea876cfff64feac80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a9005480e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918068be96b3276d6b37b3e075370fd8f677b406403548b5cfb4dad670cba871924f8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(84, "4a5ad0aedda255b6fdc72dcadec874d0a4ecf6ad0db42d322cdba70554e7e308", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350adf0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b01ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"800210804e69397633b7cc5f01353feb7a30392e7180fee11bfe0a9d4077d8e92932cbf0803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80dd704a0393cb6703c5185be5b1c522cfc941df9cfc40ea8e829a7152e5eec1608050da9108f67e43f86e887efadf46652106005f82140cfda88093f27a0e67fd6d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a9005480e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918068be96b3276d6b37b3e075370fd8f677b406403548b5cfb4dad670cba871924f8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(85, "4a96bf323dcd1570729a603338f425aaaae6d228d8076d78b36ed0fb7141d1e7", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350adf0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"800210804e69397633b7cc5f01353feb7a30392e7180fee11bfe0a9d4077d8e92932cbf0803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"80804180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8050da9108f67e43f86e887efadf46652106005f82140cfda88093f27a0e67fd6d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a9005480e0d9073ef4e83545b5aa673366e86c80741a08b5681dc45a58c9c326c0d9daf9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918069a9fb37f34624e6b89827f4b8cf479bbf922e145344193d54d9b65563d36ad08010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(86, "cab983b349ed026c6a47f17277e428e43897efa4303df2dff6710fc11b728622", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350adf0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"800210804e69397633b7cc5f01353feb7a30392e7180fee11bfe0a9d4077d8e92932cbf0803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8050da9108f67e43f86e887efadf46652106005f82140cfda88093f27a0e67fd6d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918069a9fb37f34624e6b89827f4b8cf479bbf922e145344193d54d9b65563d36ad08010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(87, "98c7eefb5743fb61c855e72738756c330238ed13776130b925450e162af5df1b", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080848011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680089794666f59d195afc45eb88cdc19fb5976219a30c67173fa59ea4b09ad3e9e8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(88, "75645220bfe4d0e0f75236d0914184816453d0c99c6e090f6e8aabbeb1478765", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"8000908028c8d8515e64aa88b94657dc6442dc9feb1602a249b97cae3b576441305ea69580f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc8093e25c450e175a08d5c4bdf3ee71ac71f840b61aabc38d644e59235f944ec90c80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(89, "f0f7d81ed326f9b587c45c200998a04175b62148b88583480712b439ab0a37a8", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80009080b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081c080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b126802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80db248dd9a8ce85cbb96c709af7d0f855d0c2d07765839001ca3e619aeb4d95ad80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80c989abf8c26de90fe350762b1d6fb1ddf71c41b13123b4793a7d56a7b8c7d566804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(90, "07db3627835cddb007ca6029d8a326030a05ee125820bd65c5f75ca13c9e9525", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80009080b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80446080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd2680b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80db248dd9a8ce85cbb96c709af7d0f855d0c2d07765839001ca3e619aeb4d95ad80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9804e5acb320b896f55cd5cff0b6ba3f55e037e509e13f611914d0bb09d93f3147e80fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(91, "a3e1e51f6340b236ebcdc1ee8f64da44905bf808befebbf58092fdfe2dd8c45a", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80009080b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"801b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80db248dd9a8ce85cbb96c709af7d0f855d0c2d07765839001ca3e619aeb4d95ad80ac450c4d8fbdd116fabf1d1bd81389e36639d156616bb992c3e80926bc54b42f80179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e880fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(92, "ae617c4c3ef39158b855a1534494a72c366e90abc6ff45cd2b781cf9ce325f17", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"80009080b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80db248dd9a8ce85cbb96c709af7d0f855d0c2d07765839001ca3e619aeb4d95ad80b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e880fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(93, "ac9e5a7a6b10d78af1c000970762dc0df8f9043f77eafd9163dc3e63cf9f2dcc", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800091801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"8053c080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80cf9e6d82109110c899eff90a4111caa2f384381c884f597a1c453d0bbdfa822480b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e880fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb95808958883017365deab17598c47410dc55158d556f4b49ee4a0246cdf3ba5b288e809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(94, "4fec7f322c9df4a9a69e9b66dc850c251b0dd95f734107617fbfb75c97295f33", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d276a543d165445d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800082809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"800091801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"805bc080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680a68121188572e2e7e80261f91ce5854f73cc30b5041d0dc55e12a75206d48be580cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80cf9e6d82109110c899eff90a4111caa2f384381c884f597a1c453d0bbdfa822480b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e880fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb9580303b184451f2c64109bdf968775ad930b7227a2672614b60aae10dbcf4aa0896809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd8010af897c696d0bfd628a7d96813bd7b3bb1222d9f8bf508836a713d4b66c6ae280bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(95, "be870e97db7399a66d2a4cfe881a0dacef17c862c9ecf9358231c7960f2d0a8d", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3620dc6e976795f45e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d276a543d165445d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800091801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"8010828087ddc195e3b4d9237e7fed1086593051760ae3cf47ef9ca3dbdec0422fdf80d5809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80402380f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"805bc080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680a68121188572e2e7e80261f91ce5854f73cc30b5041d0dc55e12a75206d48be580cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80cf9e6d82109110c899eff90a4111caa2f384381c884f597a1c453d0bbdfa822480b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e880fe71a812a9086838817ee8d7beeae13aed00daa0bec81a9340737c2213d6fb9580303b184451f2c64109bdf968775ad930b7227a2672614b60aae10dbcf4aa0896809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd80a4d7f76f6fd090b20abee5a4d65e17452679c6912ac85e09e25a8bbeaa79722080bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(96, "4e8fcd921f37e2447d291f8eea9487a42256b82289655c2a1203edc0fa8f732f", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3620dc6e976795f45e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36bdd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c0ca5d4ed90bc35f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d276a543d165445d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800091801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"8010828087ddc195e3b4d9237e7fed1086593051760ae3cf47ef9ca3dbdec0422fdf80d5809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80502380520e2095f1bc5b9fbfda653446e65cd648a005f9cf673d79328d1e8fe42eb6c080f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"805bc080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680a68121188572e2e7e80261f91ce5854f73cc30b5041d0dc55e12a75206d48be580cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c807c9cd4470fa944d5459e28a2a6ae93e67b9d6d079c10917af4c674733df1b34b80df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a6806183ed46b25558d4ea6478db1042e3cb5542e928ae030ddbe974824861e4c2bc80cf9e6d82109110c899eff90a4111caa2f384381c884f597a1c453d0bbdfa822480b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e8801d03f3b796acd059cd74c4b853fe4fc97e30feab303b3787cea011c7e081df4c80303b184451f2c64109bdf968775ad930b7227a2672614b60aae10dbcf4aa0896809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd80a4d7f76f6fd090b20abee5a4d65e17452679c6912ac85e09e25a8bbeaa79722080bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(97, "a1ac381aafa26f1e38db92052a2dec8351a6130f68dd96a50a2437891bb4b228", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3504d0e2f032f8fb60000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3620dc6e976795f45e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c0ca5d4ed90bc35f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d276a543d165445d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80002880a18c99579083e06002d88611e5d819f73880d14292f528ce208f4a31fb74dcaf8091ecc4849bb56f990174440714e8f633bcdf11570802376ce3384e398cb3f39e", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"800091801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"8010828087ddc195e3b4d9237e7fed1086593051760ae3cf47ef9ca3dbdec0422fdf80d5809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80502380520e2095f1bc5b9fbfda653446e65cd648a005f9cf673d79328d1e8fe42eb6c080f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"805bc080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680a68121188572e2e7e80261f91ce5854f73cc30b5041d0dc55e12a75206d48be580cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c800c04a15184b3e8a99683ad55ae2394a88bc6b37ac85606674f460ec01862518880df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680c7b8794c5dd8078bea433732afae6f91f59a716d700594c0492e4d14a4232d6380cf9e6d82109110c899eff90a4111caa2f384381c884f597a1c453d0bbdfa822480b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e8801d03f3b796acd059cd74c4b853fe4fc97e30feab303b3787cea011c7e081df4c80303b184451f2c64109bdf968775ad930b7227a2672614b60aae10dbcf4aa0896809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd80a4d7f76f6fd090b20abee5a4d65e17452679c6912ac85e09e25a8bbeaa79722080bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(98, "e6f9c099aad295807ba88685f2648106b3827a40b88ae69ceb38b50cac1fc871", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3504d0e2f032f8fb60000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3620dc6e976795f45e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c0ca5d4ed90bc35f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cca24f559b81f161000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d276a543d165445d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80002880a18c99579083e06002d88611e5d819f73880d14292f528ce208f4a31fb74dcaf8091ecc4849bb56f990174440714e8f633bcdf11570802376ce3384e398cb3f39e", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800891801df5a0986c26f435abf53b0f3cceb325fa75215ff44872f2d98d0841d554bd75801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"8010828087ddc195e3b4d9237e7fed1086593051760ae3cf47ef9ca3dbdec0422fdf80d5809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806fa687070fef5cfac06d7cd36a1ceff49f3b291e058b203f1f37d3db57aa50cf80d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80502380520e2095f1bc5b9fbfda653446e65cd648a005f9cf673d79328d1e8fe42eb6c080f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"805bc080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680a68121188572e2e7e80261f91ce5854f73cc30b5041d0dc55e12a75206d48be580cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c800c04a15184b3e8a99683ad55ae2394a88bc6b37ac85606674f460ec01862518880df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff802a5c23712605db3d526b86ee3ca151f783b2940fafdf7d8c34e138e4941583a680c7b8794c5dd8078bea433732afae6f91f59a716d700594c0492e4d14a4232d6380ba66995a4b3ebf1a8d1790f7704e6cb669bd22b84a849f37c64896b0a7b57fc880b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e8801d03f3b796acd059cd74c4b853fe4fc97e30feab303b3787cea011c7e081df4c80303b184451f2c64109bdf968775ad930b7227a2672614b60aae10dbcf4aa0896809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd80a4d7f76f6fd090b20abee5a4d65e17452679c6912ac85e09e25a8bbeaa79722080bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(99, "50ca34086ca6894bd58cc772db1e58c29b8fd80006f29943801813d6c9a3d765", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3504d0e2f032f8fb60000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506063c3c4b380762000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3620dc6e976795f45e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c0ca5d4ed90bc35f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cca24f559b81f161000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d276a543d165445d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80002880a18c99579083e06002d88611e5d819f73880d14292f528ce208f4a31fb74dcaf8091ecc4849bb56f990174440714e8f633bcdf11570802376ce3384e398cb3f39e", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8002018048eaa995a2bd759c6f3d71c2a5bba09df95b021401399e9a67ac6ffd2a7edd228077e15bf2d309885c6146bf04a0a564aae51d00be986c202e5ddd0bcd1cf83b13", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800891801df5a0986c26f435abf53b0f3cceb325fa75215ff44872f2d98d0841d554bd75801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"8010828087ddc195e3b4d9237e7fed1086593051760ae3cf47ef9ca3dbdec0422fdf80d5809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806efef5b40140fbe61f34e7ecf285d9a38400c9448d70d5b3d871b0812904e41280d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80502380520e2095f1bc5b9fbfda653446e65cd648a005f9cf673d79328d1e8fe42eb6c080f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"805bc080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680a68121188572e2e7e80261f91ce5854f73cc30b5041d0dc55e12a75206d48be580cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c800c04a15184b3e8a99683ad55ae2394a88bc6b37ac85606674f460ec01862518880df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8081e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef2802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80460c8f46364ccb2ea354b431415940c4cb35be6f4c706bbe9f87e14ef3fa1a2880c7b8794c5dd8078bea433732afae6f91f59a716d700594c0492e4d14a4232d6380ba66995a4b3ebf1a8d1790f7704e6cb669bd22b84a849f37c64896b0a7b57fc880b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580179b8b59c5d16586932adadb1677eb88be82fac6016574c00f2b7d0552ef8226804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e8801d03f3b796acd059cd74c4b853fe4fc97e30feab303b3787cea011c7e081df4c80303b184451f2c64109bdf968775ad930b7227a2672614b60aae10dbcf4aa0896809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd80a4d7f76f6fd090b20abee5a4d65e17452679c6912ac85e09e25a8bbeaa79722080bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +(100, "3a23b69cb1531afeb6062a6d0548bb61c5ea7a19c0b6adac9f820a68d24c3b77", &[ +"330965faf45f0f16000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"330e72d371f90b33000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34df0979c61e5050000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"34fc1bc25c1a0256000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35001ffae5405ae545000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500644d464b2ada3f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3500660717b7e77744000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350341bfbb0a4e9232000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3503e11458bdb7d23b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3504d0e2f032f8fb60000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505383ae310c5170d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3505ca40f14443730e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506063c3c4b380762000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3506c834eadafc6027000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35070f195e2e239e54000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"35071e8bab8df16b37000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507b154ae2c3eac52000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3507dce6c74c4c4b25000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350831b37c29790042000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350954dd9f53cf8c26000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350986912843223804000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509bec0ea9167f851000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509c353c0737ed329000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3509d2792f8bd4c305000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b1c010b3bff4908000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b5f9185eb807358000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7388d1b4acc847000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350b7abb205636532e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350bc52fb6d756d72a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c507d3bfbfd3f24000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c62a803faa4cc30000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350c9d27e0a04ef82d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350d454046f82f9a48000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dd9e89b24ae1c57000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350dfbd1761799352b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"350e7397d1da2f934a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360d969b0e48cab707000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"360e76f06ebd150314000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361526abea1f15b63d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361b534b9db2069d34000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f6295e49ed2492c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"361f803a716bd3b906000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3620dc6e976795f45e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36233f9722d30ebf31000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362549884eecaf5b4d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3625d85e1a2f8d065b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"362e391cb054825c59000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36351b19f226feaa18000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3648917b575ca3da4e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"364ecfc48eeb4c4c1f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365232b38c69c55b55000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3653cb1f00942ff401000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3654b59c80ee3d6140000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655c2a66f0d62cb63000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3655d945662af3f54c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"365a72f5787cb1a035000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"366e51c7f40142a143000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3674d8ea3f6de8f43c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"367a9a93920005ae1d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3681a5fc457aa15c36000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368763d79d01484e0c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368b52bf1678443909000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"368f1513cb50220646000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369190c148ccde2019000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3693d8a4ce7799c00b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36981f68b97df47f41000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36988731014ffd2c3e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369892d44badd6af13000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"3699714fc5c21da95c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"369f0caa17b4771b4b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a155a227f42ece22000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a35891bef7ae6139000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a723b5515886f73a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36a97cdde594c82e23000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36af5aee76b6da942f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b274250e6753f00a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b27f1eaef06bb903000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b2dcce60f37a2702000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b3c130b5564d5d5a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36b444df785313ea49000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c0ca5d4ed90bc35f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36c5c8b4e6df936d28000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cca24f559b81f161000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ccada06515787c10000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cda15e347d758120000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36cfe8bf76ba27f01e000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d276a543d165445d000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d703a71c18388a53000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36d9675cf95dcf1a11000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36def25cfda6ef3a00000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36e76a9656c0445715000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ea158dbfc2e1de12000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ec63ce7d367c571b000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ed79b7ec00219e0f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ee89fbf5fe7bf01c000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f0427aaa15e6e138000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f163007fa5a70017000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f476545300866c4f000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36f85e685c16504e1a000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"36ff66b7ca63162821000000f4729b675f958ea8b142b18b8a20a26c703633f8d8ec8653a7043ddc3ca20922", +"800009803c07fdae738e5a90e9c773e644ca9830ff4106c78deb999c49b06a285e9351c3809260ef60e9cd811583eafe7b1c95b0a4dd542621a2a39c38479ca994be40284c", +"80002880a18c99579083e06002d88611e5d819f73880d14292f528ce208f4a31fb74dcaf8091ecc4849bb56f990174440714e8f633bcdf11570802376ce3384e398cb3f39e", +"80003080fdba8764e3fb4b1c4b9864eefd272d3c58627fb8ede7b797cb07b0434a7fbf658026e9b7889f608274e4ee4fb842281af8cdeb14c51ef6add7c7c9fd974468003c", +"800064803b37982828bc05d280cf9b5abf56a7499db46abc478612309b8f62fd78760a0980950e488fd69cc8a099f8ef82654a0f315c7cc77e8ca402fb1ee63257703f3a91809221c38f84ce1b1222666f26bb2e3002ad7844286c3370a81665fbbd15186169", +"8001088048a3f89999e1f1a682e87a2a9e27de527c7924fae99dd7741c56643009750f658068f7f60f7408917bd088b9d692438824dadc31a6989db1e4b4f641dca2bd71fd", +"800180805ec22d1a589102212487dfb0992d09b0377e5d14464b247592068bc4566573d280c6f4fcca50e776a7ced6a9570c03c2270d5c8cac317d227e5ff16c36dcf039e4", +"8002018048eaa995a2bd759c6f3d71c2a5bba09df95b021401399e9a67ac6ffd2a7edd228077e15bf2d309885c6146bf04a0a564aae51d00be986c202e5ddd0bcd1cf83b13", +"800210801d28e528ad66024b83d96f8de8945178450173ecc5e3daf793b4b58e6c56b6398065bb8364425107d0c20e3ecb1acc16ba2708edccedc57e3ee33672ba5b68230e", +"8002108073b50b0c8d753c7b7924f04616837dfbc3f41b9540d44a138003c76cb6037767803ee8d587897b72a4a5cd90bb0560139350532ee434cc4c79d652b39491a884f9", +"80040880978573f60ea6b4ea22f6e123c3ddb1dba8d467cc8692dd6505585c06af8d911a80fb3baf251bc07c61f41ddf36c4236fa3c729e0ec69d9440f6d28366ffb2b3a12", +"8004408021b561044d3f715c006840fc152fdb0cf3664cc9ee2258932d213a0f418b63928098ec96fa9f9d7b4ab57b392c42739d12e9d189f08a459c401db647daeb993586", +"8008028070f87082dacff6d3fc4c29eb3b528c7715996838ec218673c1a23856f634fa5980f5f339ea9a1cba05b3c82a8df5caeb5c2ba385d5b88eb13ac081b2ec947b33e7", +"800891801df5a0986c26f435abf53b0f3cceb325fa75215ff44872f2d98d0841d554bd75801202603992f87ffd1b331ecff6286dcb3a10158df4d90547559f3c4a2e420d7a80b8a5f657163084109a89c63d529bd646d8f03a805005d87438803c7de0c21db680f11d188dda110f1c765398d003adbe6eca8035a2fa678406ce8abcae67442b0f", +"800a00808f8a3b21891f572172bd56220186493255cb26c89d96a1fb042d3bdf16dbbbfa80bbf52b48d123774f61c0afa6e5d8997fdfb368570853ecb720eb53855dbd57c0", +"800b00800c686fd9f6dbafdc0e30fc1b1433c3095e6421552ef64fd8ecd39a24e702451b80a23eee3e1343850de4884f1b9e5a79eda441c810b3c7828630687555dabb329e804c35f4cf81164836355a13a7136ed623d1661c12d454000a55e22a3cf31ac997", +"800c258064240a40f9e8ac3663a71ad8c939a30cfabce30bc437e87860de88dc0e01c391805f8b81822a3a7da05050e14a02f05059cf85aed792912d63e2ace04f840aa5ef80990f02858526dc89d11726f71540519b6fbfbc3d2d7fc6bb266e3bc5d4afd8f880e222fad3d8c35686bc05731b8e44ec9b9e3e982f8ad7e54507f02eb7e9842a2b80be59474dcf0ae8e00e15bb1344b249dfad37be8e4b9ec12e938c8ddc10b773a1", +"80100480e30bf622c50fe9a8fe454fb366988271896cddd1761bd4aa0600e4eb3bebbc59805699fdd817b520d8654dfd55d9315fd1d42c16f033d23a53740788affa05c1d2", +"8010828087ddc195e3b4d9237e7fed1086593051760ae3cf47ef9ca3dbdec0422fdf80d5809b569662f10cb0da1fad3c1925f24eb9d0ef50acadb48d9dacd90543ba589a8980a8979fa6b27b899b4c3a9b5e3582195dacb1b4bf803dba8881848c65324b9c4a", +"801a6280e2fd488c284b0b89c7172d86b7e0ba58a0400d54f13c887dab21fa274e81f98680e21d1c44044fe853b6ffe8eddb08cb351e633be8237ee62d31704e09509f8da280a7e2caa08deb0caf0e7c0fd0957ae930d7f031e5ed4d2696a23195768ddc93d5806efef5b40140fbe61f34e7ecf285d9a38400c9448d70d5b3d871b0812904e41280d062795aed20f6dba752613cd0fc916018fdddc775e1412fefe7f243e15d3f2980af2fb6f70525f21c9e8d65db1a04b748d066bf4676a965c6d37bb6ad578a0e8a", +"802010804b3f0a374f23be6a4953fc994ae308ecd2a1eacf210f8c109fd37b1d79661e918022a6477c8f2130e75e2a9a379384bf2bba7edb5d418043290ec487b22803c2cb", +"80348880dd4c285b40aab18b4beaf60e780d83115e9ed4da663aa28f9690876d677d956b80258c21c997c7c69e407e036f37cc6581d6ffcb17ed09ce4c1ebbee7b0c9b4f968060e0e545f3d2f4f253782b1e34ba92645802f20e2799b36fcac970baa5eebfa78023ba132c7f01fea45ed9fa8098e59e06b6ff6e86256bd32c23c2401595394d4680d83ddd1730b455ecc731a048fb20334d2c8d0955d92297961baeac88bf3272ee", +"803b5380f0cedcdee2617db27c7e8cbcc0271011410423fbef66c80771e181919e61308e8075f3874e71ad137809fa33336c3b1b1c1c75969c4c44de835d9955126c8f970080196f8f1a834101d28512ee8b31dc8b78ec706853e26a14845675edc08515ac3f8063a5b7203043ac1d20e87a231a08f4c7d6d1dd89fb6d355f9f366dfa53f79b90801f6576aedc0634a4b5172e787cc50286b60d23d7c398aee2fd5c432c8acd678b804095cee52485ed5e5ddd962d8568d03a5a1db134ee11fcaf3f7976e6ae777f99804ef82319bcd2434518335bccb68342a1836e1dc10c541b712f0940396b34f4158058fc2e1f312065ffcf9a9da79a37df4ff2a202e2bd7be09ea9599ea5a2786c6e80b41bcff09c12dee7ea9e02804052db0642b87ea1178204b9371f4ff874a222ff", +"80500e8047795f007ce96848bca5918c8d9d26368404904bc883af0631adc155bbb111aa801fe1021789299240c30f1129390c5e82bc36e6de97844588716101044c1a287c807c7ca05df58b2bbfc2a52c7df4ffc116b23b5b662914985e47ae675db2f1e5e98021a2b46c632d08822c6a9f838312c35bf9484eab9a6adfcc0d124cf9c254ed9780ef447f45f3fa601345a1908c3de25b71c260781c613f366d367fb2c4e8236e95", +"80502380520e2095f1bc5b9fbfda653446e65cd648a005f9cf673d79328d1e8fe42eb6c080f973d54a58252d2a5fa41596c30a0eb9ed9c0c294ff3e6d27056893517a6a293806cee9f553028df63145eb7919de1ed5d06463c566da2784185a6d4968a21d18d8088638544b5666f00de2f386910fe9d8578021d79a426b1baa98ad28acbefb71780db4e8710ed9e05c0363dd7c165db95504b9d977004bc95dc1bb928aff5fb0011", +"805bc080966da5003b69b16940e61c4ff79c938b4e500273194efedcc8f95e9cd254863380e1ebc5a44b577bb781d2992c0070f4a6aceff6b853fb6128e688b51d61a7f7c680a68121188572e2e7e80261f91ce5854f73cc30b5041d0dc55e12a75206d48be580cfc72dac419fdf4dfc43916fa9146a49507eeee5674c2a500ee69950d5eef65480d2021046b527da079f18e82d7139322192321ca0724d20f65527cff16f3da30480cd94faadc04c34ccd6fb033c1437b1832547a3bd7b49075c45328d1f7c5b201b801a388efd7762b7d4f42cf95a8f12ebaeb84dc181d9ef1bf764a0973ed7936f72", +"806000801d1a40bba8b805e94d1f1e5abecfb85c8ff610dbf41f96f75e6fea4ecf85d13e808a3052b45cf25c1ae299da5853afe561a275098f1665132b5d4b8ae7946f806b", +"808004802ba56636c0f3b16dcb9b329f7ea583532efe941d004a6fe25e83a5cd73e4e0eb803d715494fc555ca8ff67c956c910bbfd79c0f4b9e462b1fb00e45f5252547d4c", +"8080a48011743a145bd135e09d0e877e18cf620ab0002186e5f34ea3930f07d0779d9c5d8051d6b67790642bab25003a22348448862a5736558af4493ebfd2383a6e84a97c800c04a15184b3e8a99683ad55ae2394a88bc6b37ac85606674f460ec01862518880df0a8a43bb2d634cbce12aed710b9e07024e640904b39a1527801a362f62fb1a", +"8083e080eee48c0992858e0e7d169f61eed4e6bb165a5818ecf1704c1e1ba76a73a60ef28064a9bd94877cf964137ba78535452374ef91434cb5980a2782945a34878479da802bfa4b7ce27ba234d6329d1ef42f5c1ca1784a76b0c1ab03c7805e0e18d0b12680fa952eb660aed3c7dc6b8edada1a002eaed8fac98bdcc6cbd0b66df07eb04cf2802049d9671a7644a4011fa6b8c733ab72f38d13572f3fee52141acdd98c0fa4958019218e901207479d5b2c7b56b44c9923b02b90fa87a548e6a0c36e7f6f15630d", +"8088928087c0d1937f391842b64a28f7af325c718fdd164fe7947613eb68dc926bd8287d80d31a622e6218fdf9c0c124872741180eae9198e22aa4168b516e9bdee352853e8040030c7172201a22391f97749b2cdb98165afdde8d0024d6e110d86ea668a82d8021ff8d6c78ab2724d505f3f806e5f076395901969d39e5806b071d799d16dbc9803892fff661312871f069a1a7cde455fc5191cabdd2c4a9e2d9670b0d9443c385", +"80904180f0eac025c8f43de3b9ea20a2445e02f2d2845401aded6dee7c6d6ca9edf60a0180402a347af97153e731ad0e5d3acb6fd538a0fa97730b9e570eb945551f2b7f0b80a08b1ec5bbb2ad535f083a359c6911f37eaff3851d7bd64094ed4d5fa39c001a806b45b6cb17339318e73756539bb447c84f729b3a13cbb608617d05cb78be52be", +"809302803644638584c9e36aff25b1da0e15abee3940c13651820bcf1424fcb21f799aed809e598d62614767572b958b1b664e7905be721024d7782bbf4ffb52684ea87ea080aef06d49d4722ceea3b435d803dc032415be5603ac5c8fe7ebb17962782430ac8046034b9c31d5dba931d90edc3f0e685b1375e70c901959e605e5ea0166568e6d80f8882a2a94f6dee983f906b1411730dff25b5a61ffe58d81862e8076751ceba9", +"80c46080ab97bd8a801e8ee08e981a0027711c7f07b526cc279411d1c3c178a2c9535f4c80393739790fc85d74c93825e6bb6fb8887b695579c3f272ece422fe898ffdfd26806b7060002c34c7674c07fb4041122ce0fcfccfd614f6af54ac6fe33b3fc0019c80b75964048acbc449d79d05f2f828a34d451548985f6e52c6c565c38365ad8b738086f8a367a41d659bf95bddf03fb2bc4e589cfdffad4f3c68fc07a7f1ce437589", +"826f084080fbf037bbe3b6397c895187d7f587076ec4c64c52ab346e64951803062ed3cb5d805a967c33d3d57d51c07881d92a84b13bda05190a9f1c7e22ad7193d550df5fd7", +"bf01cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3ffff80460c8f46364ccb2ea354b431415940c4cb35be6f4c706bbe9f87e14ef3fa1a2880c7b8794c5dd8078bea433732afae6f91f59a716d700594c0492e4d14a4232d6380ba66995a4b3ebf1a8d1790f7704e6cb669bd22b84a849f37c64896b0a7b57fc880b1799f10898e3d56d2326ad758a591e5347e9f0278c6d5727db9fbfadea65ac580ba1969805848ef9dfea6a52b7e58f48bfe375f6048eb3785f5ff384f4813ca49804c2f428a882271aaa8ee024dca2efa8158967882a9bb52d95c82db6a3193b38480649946c289dd94ee6b5cdcc97192c99f872c109d526ea77940eee6964f499ef88003417ebd5d827d968c1af84849a9e1045701025d77d2c2be7948bdf599a90054807e8d535456d5bafc507ca49c7d17ead1572d061781ff55301fb8f99cc14afbd9805cf5f7beac71ab67bd32c28ddf7c204de8d483f648c8399de3185c0a8ff057e8801d03f3b796acd059cd74c4b853fe4fc97e30feab303b3787cea011c7e081df4c80303b184451f2c64109bdf968775ad930b7227a2672614b60aae10dbcf4aa0896809d5a8db9556cd997a36aafbc557f6c7adcb7c2f61592d8d1c87b4add3dd7cc918003ab0095f2261e97db0d4b46a574908596a716a8a233b6e4c55c3f86f35650fd80a4d7f76f6fd090b20abee5a4d65e17452679c6912ac85e09e25a8bbeaa79722080bf5b4d3f1c7fe43d7ecec7f558d79f556bd88f0de97810bc467fab3947b28ce6", +"c10100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040661757261200d00000000000000", +]), +]; diff --git a/pallets/author-noting/src/tests.rs b/pallets/author-noting/src/tests.rs new file mode 100644 index 0000000..4e90837 --- /dev/null +++ b/pallets/author-noting/src/tests.rs @@ -0,0 +1,791 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{mock::*, ContainerChainBlockInfo, Event}, + bounded_collections::bounded_vec, + cumulus_primitives_core::ParaId, + frame_support::{ + assert_ok, + dispatch::GetDispatchInfo, + inherent::{InherentData, ProvideInherent}, + traits::UnfilteredDispatchable, + }, + frame_system::RawOrigin, + hex_literal::hex, + parity_scale_codec::Encode, + sp_consensus_aura::{inherents::InherentType, AURA_ENGINE_ID}, + sp_core::H256, + sp_runtime::{ + generic::DigestItem, + traits::{BlakeTwo256, HashingFor}, + }, + test_relay_sproof_builder::{HeaderAs, ParaHeaderSproofBuilder, ParaHeaderSproofBuilderItem}, + tp_traits::GetCurrentContainerChains, +}; + +#[test] +fn test_author_id_insertion() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 13u64, + latest_slot_number: 1u64.into(), + }) + ); + }); +} + +#[test] +fn test_author_id_insertion_real_data() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| { + // Statemint data: + // Block: 3,511,063 + // Slot: 140,006,956 + // RelayHash 0x5ea27df08fe09a82b5e835d4fa67735d0fbdf8d97b9c382f0af7b9c9c92a8545 + let statemint_data = hex!( + "5d1b54ce2845dedd7f43805849747c44388b7b7cc84dc5083815cc2b58b513145e4cd6000a98bf + 27921e16366f5a2a388595f87744608684f43ff613026241634390d0c28a9dee52544070b989c71634 + db54222b86391a75fa37d12544e7022bcd3cd42a080661757261202c56580800000000056175726101 + 018fb36de33276e8d54f77ea0a006ed7ab97b8d0aad00869f7ce6a5709eb1fc3256428b8b2428a2a3e + c4fa1c1058ab0e33c5a6b2b5789ab7b3e0accaeccafb4506" + ); + + match relay_block_num { + 1 => { + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::AlreadyEncoded(statemint_data.to_vec()), + }; + sproof.items.push(s); + } + _ => unreachable!(), + } + }) + .add(1, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + // Our mock author fetcher will just note the slot + Some(ContainerChainBlockInfo { + block_number: 3511063, + author: 140006956, + latest_slot_number: 1u64.into() + }) + ); + }); +} + +#[test] +fn test_author_id_insertion_many_paras() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + // Since the default parachain list is vec![1001], + // we must always include a sproof for this para_id + let slot: InherentType = 10u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + 2 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 2, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + + let slot: InherentType = 14u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1002.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + // Writing to this pallet storage will only change the sproofs of the next block, + // not the ones of the current block + MockData::mutate(|m| { + m.container_chains = bounded_vec![1001.into(), 1002.into()]; + }); + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 10u64, + latest_slot_number: 1u64.into() + }) + ); + assert_eq!(AuthorNoting::latest_author(ParaId::from(1002)), None); + }) + .add(2, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 2, + author: 13u64, + latest_slot_number: 2u64.into() + }) + ); + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1002)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 14u64, + latest_slot_number: 2u64.into() + }) + ); + }); +} + +#[test] +#[should_panic(expected = "Invalid relay chain state proof")] +fn test_should_panic_with_invalid_proof_root() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + // Insert an invalid root, not matching the proof generated + .with_overriden_state_root(H256::default()) + .add(1, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 13u64, + latest_slot_number: 0u64.into() + }) + ); + }); +} + +#[test] +#[should_panic(expected = "Invalid proof provided for para head key")] +fn test_should_panic_with_invalid_proof_state() { + let sproof_builder = ParaHeaderSproofBuilder::default(); + let (_, relay_chain_state) = sproof_builder.into_state_root_and_proof(); + + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + // Insert a proof, not matching the root generated + .with_overriden_state_proof(relay_chain_state) + .add(1, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 13u64, + latest_slot_number: 0u64.into() + }) + ); + }); +} + +#[test] +#[should_panic(expected = "Invalid proof provided for para head key")] +fn test_should_panic_with_proof_for_not_including_required_para() { + // Since the default parachain list is vec![1001], + // we must always include a sproof for this para_id + let slot: InherentType = 10u64.into(); + let para_id_1001_item = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header:: { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + let mut proof_item = ParaHeaderSproofBuilder::default(); + proof_item.items.push(para_id_1001_item.clone()); + + // However we insert a new para in the state. The idea is that the proof we + // will pass is for this new paraId, and not 1001. Passing 1001 is required so + // we should see the node panicking. + + let slot: InherentType = 14u64.into(); + let para_id_1002_item = ParaHeaderSproofBuilderItem { + para_id: 1002.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header:: { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + proof_item.items.push(para_id_1002_item.clone()); + + // lets get the generated proof here. However we will modify later on the proof we pass to include para id 1002 + let (root, proof) = proof_item.clone().into_state_root_and_proof(); + let db = proof.into_memory_db::>(); + let backend = sp_state_machine::TrieBackendBuilder::new(db, root).build(); + + // this should contain both keys (1001, 1002). but we will now generate a proof without one of the keys (1001) + let mut relevant_keys = proof_item.relevant_keys(); + // remove para 1001 + relevant_keys.remove(0); + // re-generate the proof only for para 1002 + let proof = sp_state_machine::prove_read(backend, relevant_keys).expect("prove read"); + + // We now have a state containing 1001 and 1002 paras, but only 1002 is passed in the proof (when 1001 is required) + BlockTests::new() + .with_relay_sproof_builder(move |_, relay_block_num, sproof| match relay_block_num { + 1 => { + // We guarantee we generate the same DB by constructing the same items + sproof.items.push(para_id_1001_item.clone()); + sproof.items.push(para_id_1002_item.clone()); + } + _ => unreachable!(), + }) + .with_overriden_state_proof(proof) + .add(1, || {}); +} + +#[test] +#[should_panic(expected = "Invalid proof provided for para head key")] +fn test_should_panic_with_empty_proof() { + // Since the default parachain list is vec![1001], + // we must always include a sproof for this para_id + let slot: InherentType = 10u64.into(); + let mut para_id_1001_item = ParaHeaderSproofBuilderItem::default(); + let mut proof_item = ParaHeaderSproofBuilder::default(); + + para_id_1001_item.para_id = 1001.into(); + para_id_1001_item.author_id = + HeaderAs::NonEncoded(sp_runtime::generic::Header:: { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }); + proof_item.items.push(para_id_1001_item.clone()); + + // lets get the generated proof here. However we will modify later on the proof to not include anything + let (root, proof) = proof_item.clone().into_state_root_and_proof(); + let db = proof.into_memory_db::>(); + let backend = sp_state_machine::TrieBackendBuilder::new(db, root).build(); + + // Empty relevant keys + let relevant_keys: Vec> = Vec::new(); + // re-generate the proof for nothing + let proof = sp_state_machine::prove_read(backend, relevant_keys).expect("prove read"); + + // We now have a state containing 1001, but an empty proof will be passed + BlockTests::new() + .with_relay_sproof_builder(move |_, relay_block_num, sproof| match relay_block_num { + 1 => { + // We guarantee we generate the same DB by constructing the same items + sproof.items.push(para_id_1001_item.clone()); + } + _ => unreachable!(), + }) + .with_overriden_state_proof(proof) + .add(1, || {}); +} + +#[test] +#[should_panic(expected = "Container chain author data needs to be present in every block!")] +fn test_not_inserting_inherent() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .skip_inherent_insertion() + .add(1, || { + assert!(AuthorNoting::latest_author(ParaId::from(1001)).is_none()); + }); +} + +#[test] +#[ignore = "used to generate benchmark data"] +fn encode_proof_for_benchmarks() { + println!("pub const ENCODED_PROOFS: &[(u32, &str, &[&str])] = &["); + + for x in 0u32..=100 { + let mut sproof_builder = ParaHeaderSproofBuilder::default(); + + for para_id in 0..x { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: para_id.into(), + + // TODO: this header can be arbitrarily large, because "digest.logs" is an unbounded vec + author_id: HeaderAs::NonEncoded(dp_core::Header { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof_builder.items.push(s); + } + + let (root, proof) = sproof_builder.into_state_root_and_proof(); + + println!("({}, \"{}\", &[", x, hex::encode(root),); + + for x in proof.iter_nodes() { + println!("\"{}\",", hex::encode(x)); + } + + println!("]),"); + } + + println!("];") +} + +#[test] +fn test_set_author() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 13u64, + latest_slot_number: 1u64.into() + }) + ); + assert_ok!(AuthorNoting::set_author( + RuntimeOrigin::root(), + 1001.into(), + 1, + 14u64, + 14u64.into() + )); + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 14u64, + latest_slot_number: 14u64.into() + }) + ); + System::assert_last_event( + Event::LatestAuthorChanged { + para_id: 1001.into(), + block_number: 1, + new_author: 14u64, + latest_slot_number: 14u64.into(), + } + .into(), + ); + }); +} + +#[test] +#[should_panic(expected = "DidSetContainerAuthorData must be updated only once in a block")] +fn test_on_initalize_does_not_kill_and_panics() { + BlockTests::new() + .skip_author_noting_on_initialize() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + crate::DidSetContainerAuthorData::::put(true); + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || {}); +} + +#[test] +fn test_header_non_decodable_does_not_insert() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::AlreadyEncoded(hex!("4321").to_vec()), + }; + + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + assert_eq!(AuthorNoting::latest_author(ParaId::from(1001)), None); + }); +} + +#[test] +fn test_non_aura_digest_does_not_insert_key() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + // we inject a non-aura digest + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime( + [b'a', b'a', b'a', b'a'], + slot.encode(), + )], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + assert_eq!(AuthorNoting::latest_author(ParaId::from(1001)), None); + }); +} + +#[test] +fn test_non_decodable_slot_does_not_insert_key() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: Default::default(), + state_root: Default::default(), + extrinsics_root: Default::default(), + // we inject 1u8 slot, but inherentType is expected so it should not decode + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, 1u8.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + assert_eq!(AuthorNoting::latest_author(ParaId::from(1001)), None); + }); +} + +#[test] +fn weights_assigned_to_extrinsics_are_correct() { + new_test_ext().execute_with(|| { + assert_eq!( + crate::Call::::set_author { + para_id: 1.into(), + block_number: 1, + author: 1u64, + latest_slot_number: 0u64.into() + } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::set_author() + ); + + let sproof_builder = ParaHeaderSproofBuilder::default(); + + let (relay_root, relay_chain_state) = sproof_builder.into_state_root_and_proof(); + frame_support::storage::unhashed::put(MOCK_RELAY_ROOT_KEY, &relay_root); + + let mut inherent_data = InherentData::default(); + let system_inherent_data = tp_author_noting_inherent::OwnParachainInherentData { + relay_storage_proof: relay_chain_state, + }; + inherent_data + .put_data( + tp_author_noting_inherent::INHERENT_IDENTIFIER, + &system_inherent_data, + ) + .expect("failed to put VFP inherent"); + let inherent_weight = AuthorNoting::create_inherent(&inherent_data) + .expect("got an inherent") + .dispatch_bypass_filter(RawOrigin::None.into()) + .expect("dispatch succeeded"); + + assert_eq!( + inherent_weight.actual_weight.unwrap(), + <() as crate::weights::WeightInfo>::set_latest_author_data( + ::ContainerChains::current_container_chains().len() as u32 + ) + ); + }); +} + +#[test] +fn test_kill_author_data() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 13u64, + latest_slot_number: 1u64.into() + }) + ); + assert_ok!(AuthorNoting::kill_author_data( + RuntimeOrigin::root(), + 1001.into(), + )); + assert_eq!(AuthorNoting::latest_author(ParaId::from(1001)), None); + System::assert_last_event( + Event::RemovedAuthorData { + para_id: 1001.into(), + } + .into(), + ); + }); +} + +#[test] +fn test_author_id_insertion_not_first_log() { + BlockTests::new() + .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { + 1 => { + let slot: InherentType = 13u64.into(); + let s = ParaHeaderSproofBuilderItem { + para_id: 1001.into(), + author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::< + u32, + BlakeTwo256, + > { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![ + // Dummy item before aura log + DigestItem::PreRuntime([0; 4], vec![]), + DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode()), + ], + }, + }), + }; + sproof.items.push(s); + } + _ => unreachable!(), + }) + .add(1, || { + assert_eq!( + AuthorNoting::latest_author(ParaId::from(1001)), + Some(ContainerChainBlockInfo { + block_number: 1, + author: 13u64, + latest_slot_number: 1u64.into() + }) + ); + }); +} diff --git a/pallets/author-noting/src/weights.rs b/pallets/author-noting/src/weights.rs new file mode 100644 index 0000000..ea0c992 --- /dev/null +++ b/pallets/author-noting/src/weights.rs @@ -0,0 +1,155 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_author_noting +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `tomasz-XPS-15-9520`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_author_noting +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_author_noting. +pub trait WeightInfo { + fn set_latest_author_data(x: u32, ) -> Weight; + fn set_author() -> Weight; + fn kill_author_data() -> Weight; +} + +/// Weights for pallet_author_noting using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: AuthorNoting DidSetContainerAuthorData (r:1 w:1) + /// Proof: AuthorNoting DidSetContainerAuthorData (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Registrar RegisteredParaIds (r:1 w:0) + /// Proof Skipped: Registrar RegisteredParaIds (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainSystem ValidationData (r:1 w:0) + /// Proof Skipped: ParachainSystem ValidationData (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CollatorAssignment CollatorContainerChain (r:1 w:0) + /// Proof Skipped: CollatorAssignment CollatorContainerChain (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AuthorNoting LatestAuthor (r:0 w:100) + /// Proof: AuthorNoting LatestAuthor (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// The range of component `x` is `[0, 100]`. + fn set_latest_author_data(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `427 + x * (73 ±0)` + // Estimated: `1912 + x * (73 ±0)` + // Minimum execution time: 7_767_000 picoseconds. + Weight::from_parts(7_985_000, 1912) + // Standard Error: 125_649 + .saturating_add(Weight::from_parts(19_274_325, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 73).saturating_mul(x.into())) + } + /// Storage: AuthorNoting LatestAuthor (r:0 w:1) + /// Proof: AuthorNoting LatestAuthor (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + fn set_author() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_877_000 picoseconds. + Weight::from_parts(8_127_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: AuthorNoting LatestAuthor (r:0 w:1) + /// Proof: AuthorNoting LatestAuthor (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + fn kill_author_data() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_190_000 picoseconds. + Weight::from_parts(7_520_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: AuthorNoting DidSetContainerAuthorData (r:1 w:1) + /// Proof: AuthorNoting DidSetContainerAuthorData (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: Registrar RegisteredParaIds (r:1 w:0) + /// Proof Skipped: Registrar RegisteredParaIds (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainSystem ValidationData (r:1 w:0) + /// Proof Skipped: ParachainSystem ValidationData (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CollatorAssignment CollatorContainerChain (r:1 w:0) + /// Proof Skipped: CollatorAssignment CollatorContainerChain (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: AuthorNoting LatestAuthor (r:0 w:100) + /// Proof: AuthorNoting LatestAuthor (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// The range of component `x` is `[0, 100]`. + fn set_latest_author_data(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `427 + x * (73 ±0)` + // Estimated: `1912 + x * (73 ±0)` + // Minimum execution time: 7_767_000 picoseconds. + Weight::from_parts(7_985_000, 1912) + // Standard Error: 125_649 + .saturating_add(Weight::from_parts(19_274_325, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 73).saturating_mul(x.into())) + } + /// Storage: AuthorNoting LatestAuthor (r:0 w:1) + /// Proof: AuthorNoting LatestAuthor (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + fn set_author() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_877_000 picoseconds. + Weight::from_parts(8_127_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: AuthorNoting LatestAuthor (r:0 w:1) + /// Proof: AuthorNoting LatestAuthor (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + fn kill_author_data() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_190_000 picoseconds. + Weight::from_parts(7_520_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/authority-assignment/Cargo.toml b/pallets/authority-assignment/Cargo.toml new file mode 100644 index 0000000..ef7acd5 --- /dev/null +++ b/pallets/authority-assignment/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "pallet-authority-assignment" +authors = { workspace = true } +description = "Authority collator assignment pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +dp-collator-assignment = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive", "max-encoded-len" ] } +scale-info = { workspace = true } +serde = { workspace = true, optional = true, features = [ "derive" ] } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +tp-traits = { workspace = true } + +[dev-dependencies] +sp-io = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "dp-collator-assignment/std", + "frame-support/std", + "frame-system/std", + "log/std", + "parity-scale-codec/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-traits/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/authority-assignment/src/lib.rs b/pallets/authority-assignment/src/lib.rs new file mode 100644 index 0000000..66a73f3 --- /dev/null +++ b/pallets/authority-assignment/src/lib.rs @@ -0,0 +1,113 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Nimbus Collator Assignment Pallet + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +use { + dp_collator_assignment::AssignedCollators, + frame_support::pallet_prelude::*, + sp_runtime::{ + traits::{AtLeast32BitUnsigned, One, Zero}, + Saturating, + }, + sp_std::{collections::btree_map::BTreeMap, prelude::*, vec}, +}; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned; + type AuthorityId: parity_scale_codec::FullCodec + TypeInfo + Clone; + } + + #[pallet::storage] + #[pallet::getter(fn collator_container_chain)] + pub type CollatorContainerChain = StorageMap< + _, + Twox64Concat, + T::SessionIndex, + AssignedCollators, + OptionQuery, + >; + + #[pallet::call] + impl Pallet {} + + impl Pallet { + /// Assign new collators + /// collators should be queued collators + pub fn assign_collators( + current_session_index: &T::SessionIndex, + queued_id_to_nimbus_map: &BTreeMap, + next_collator_assignment: &AssignedCollators, + ) { + let next_nimbus_assignment = next_collator_assignment + .map(|account_id| queued_id_to_nimbus_map[account_id].clone()); + + // Only applies to session index 0 + if current_session_index == &T::SessionIndex::zero() { + CollatorContainerChain::::insert( + current_session_index, + next_nimbus_assignment.clone(), + ); + CollatorContainerChain::::insert( + current_session_index.saturating_add(T::SessionIndex::one()), + next_nimbus_assignment, + ); + + return; + } + + // Remove value at session - 1, insert new value at session + 1 + CollatorContainerChain::::remove( + current_session_index.saturating_sub(T::SessionIndex::one()), + ); + CollatorContainerChain::::insert( + current_session_index.saturating_add(T::SessionIndex::one()), + next_nimbus_assignment, + ); + } + + pub fn initializer_on_new_session( + current_session_index: &T::SessionIndex, + queued_id_to_nimbus_map: &BTreeMap, + next_collator_assignment: &AssignedCollators, + ) { + Self::assign_collators( + current_session_index, + queued_id_to_nimbus_map, + next_collator_assignment, + ) + } + } +} diff --git a/pallets/authority-assignment/src/mock.rs b/pallets/authority-assignment/src/mock.rs new file mode 100644 index 0000000..44bf6ea --- /dev/null +++ b/pallets/authority-assignment/src/mock.rs @@ -0,0 +1,154 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use dp_collator_assignment::AssignedCollators; + +use { + crate::{self as pallet_authority_assignment}, + frame_support::traits::{ConstU16, ConstU64}, + frame_system as system, + parity_scale_codec::{Decode, Encode}, + sp_core::H256, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, + }, + sp_std::collections::btree_map::BTreeMap, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + MockData: mock_data, + AuthorityAssignment: pallet_authority_assignment, + } +); + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = (); +} + +// Pallet to provide some mock data, used to test +#[frame_support::pallet] +pub mod mock_data { + use {super::*, frame_support::pallet_prelude::*}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn mock)] + pub(super) type Mock = StorageValue<_, Mocks, ValueQuery>; + + impl Pallet { + pub fn get() -> Mocks { + Mock::::get() + } + pub fn mutate(f: F) -> R + where + F: FnOnce(&mut Mocks) -> R, + { + Mock::::mutate(f) + } + } +} + +#[derive( + Default, Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo, +)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Mocks { + pub nimbus_map: BTreeMap, + pub next_collator_assignment: AssignedCollators, +} + +impl mock_data::Config for Test {} + +// In tests, we ignore the session_index param, so changes to the configuration are instant + +impl pallet_authority_assignment::Config for Test { + type SessionIndex = u32; + type AuthorityId = String; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} + +pub const SESSION_LEN: u64 = 5; + +pub fn run_to_session(n: u32) { + let block_number = SESSION_LEN * u64::from(n); + run_to_block(block_number + 1); +} + +pub fn run_to_block(n: u64) { + let old_block_number = System::block_number(); + + for x in (old_block_number + 1)..=n { + System::reset_events(); + System::set_block_number(x); + + if x % SESSION_LEN == 1 { + let session_index = (x / SESSION_LEN) as u32; + let nimbus_map = &MockData::mock().nimbus_map; + let next_collator_assignment = &MockData::mock().next_collator_assignment; + AuthorityAssignment::initializer_on_new_session( + &session_index, + nimbus_map, + next_collator_assignment, + ); + } + } +} diff --git a/pallets/authority-assignment/src/tests.rs b/pallets/authority-assignment/src/tests.rs new file mode 100644 index 0000000..b66f13c --- /dev/null +++ b/pallets/authority-assignment/src/tests.rs @@ -0,0 +1,405 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{mock::*, CollatorContainerChain}, + dp_collator_assignment::AssignedCollators, + std::collections::BTreeMap, +}; + +fn assigned_collators_at_session(session_index: u32) -> Option> { + let assigned_collators = CollatorContainerChain::::get(session_index)?; + + let mut h = BTreeMap::new(); + + for (para_id, collators) in assigned_collators.container_chains.iter() { + for collator in collators.iter() { + h.insert(collator.clone(), u32::from(*para_id)); + } + } + + for collator in assigned_collators.orchestrator_chain { + h.insert(collator, 999); + } + + Some(h) +} + +#[test] +fn assign_collators_genesis() { + new_test_ext().execute_with(|| { + MockData::mutate(|m| { + m.next_collator_assignment = AssignedCollators { + orchestrator_chain: vec![1, 2, 3, 4, 5], + container_chains: BTreeMap::from_iter(vec![ + (1001.into(), vec![6, 7]), + (1002.into(), vec![8, 9]), + ]), + }; + + m.nimbus_map = BTreeMap::from_iter( + vec![ + (1, "nmbs1"), + (2, "nmbs2"), + (3, "nmbs3"), + (4, "nmbs4"), + (5, "nmbs5"), + (6, "nmbs6"), + (7, "nmbs7"), + (8, "nmbs8"), + (9, "nmbs9"), + ] + .into_iter() + .map(|(id, nimbus_id)| (id, nimbus_id.to_string())), + ); + }); + + run_to_block(1); + + let expected_collators: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ("nmbs9", 1002), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + assert_eq!(assigned_collators_at_session(0), expected_collators); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), None); + }); +} + +#[test] +fn assign_collators_session_one() { + new_test_ext().execute_with(|| { + MockData::mutate(|m| { + m.next_collator_assignment = AssignedCollators { + orchestrator_chain: vec![1, 2, 3, 4, 5], + container_chains: BTreeMap::from_iter(vec![ + (1001.into(), vec![6, 7]), + (1002.into(), vec![8, 9]), + ]), + }; + + m.nimbus_map = BTreeMap::from_iter( + vec![ + (1, "nmbs1"), + (2, "nmbs2"), + (3, "nmbs3"), + (4, "nmbs4"), + (5, "nmbs5"), + (6, "nmbs6"), + (7, "nmbs7"), + (8, "nmbs8"), + (9, "nmbs9"), + ] + .into_iter() + .map(|(id, nimbus_id)| (id, nimbus_id.to_string())), + ); + }); + + run_to_block(1); + + let expected_collators: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ("nmbs9", 1002), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + assert_eq!(assigned_collators_at_session(0), expected_collators); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), None); + + run_to_session(1); + + assert_eq!(assigned_collators_at_session(0), None); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), expected_collators); + assert_eq!(assigned_collators_at_session(3), None); + }); +} + +#[test] +fn assign_collators_change_nimbus_key() { + new_test_ext().execute_with(|| { + MockData::mutate(|m| { + m.next_collator_assignment = AssignedCollators { + orchestrator_chain: vec![1, 2, 3, 4, 5], + container_chains: BTreeMap::from_iter(vec![ + (1001.into(), vec![6, 7]), + (1002.into(), vec![8, 9]), + ]), + }; + + m.nimbus_map = BTreeMap::from_iter( + vec![ + (1, "nmbs1"), + (2, "nmbs2"), + (3, "nmbs3"), + (4, "nmbs4"), + (5, "nmbs5"), + (6, "nmbs6"), + (7, "nmbs7"), + (8, "nmbs8"), + (9, "nmbs9"), + ] + .into_iter() + .map(|(id, nimbus_id)| (id, nimbus_id.to_string())), + ); + }); + + run_to_block(1); + + let expected_collators: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ("nmbs9", 1002), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + assert_eq!(assigned_collators_at_session(0), expected_collators); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), None); + + MockData::mutate(|m| { + // Change key for collator 1 + m.nimbus_map.insert(1, "nmbs1-changed".to_string()); + }); + + run_to_session(1); + + let expected_collators_at_2: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1-changed", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ("nmbs9", 1002), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + // Collators in session 2 use the new keys, but collators in session 1 use the old keys + assert_eq!(assigned_collators_at_session(0), None); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), expected_collators_at_2); + assert_eq!(assigned_collators_at_session(3), None); + }); +} + +#[test] +fn assign_collators_remove_collator() { + new_test_ext().execute_with(|| { + MockData::mutate(|m| { + m.next_collator_assignment = AssignedCollators { + orchestrator_chain: vec![1, 2, 3, 4, 5], + container_chains: BTreeMap::from_iter(vec![ + (1001.into(), vec![6, 7]), + (1002.into(), vec![8, 9]), + ]), + }; + + m.nimbus_map = BTreeMap::from_iter( + vec![ + (1, "nmbs1"), + (2, "nmbs2"), + (3, "nmbs3"), + (4, "nmbs4"), + (5, "nmbs5"), + (6, "nmbs6"), + (7, "nmbs7"), + (8, "nmbs8"), + (9, "nmbs9"), + ] + .into_iter() + .map(|(id, nimbus_id)| (id, nimbus_id.to_string())), + ); + }); + + run_to_block(1); + + let expected_collators: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ("nmbs9", 1002), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + assert_eq!(assigned_collators_at_session(0), expected_collators); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), None); + + MockData::mutate(|m| { + // Remove key for collator 9 + m.nimbus_map.remove(&9); + // And remove collator 9 from assignment + let collators_1002 = m + .next_collator_assignment + .container_chains + .get_mut(&1002.into()) + .unwrap(); + assert_eq!(collators_1002.pop(), Some(9)); + }); + + run_to_session(1); + + let expected_collators_at_2: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + // Collators in session 2 use the new keys, but collators in session 1 use the old keys + assert_eq!(assigned_collators_at_session(0), None); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), expected_collators_at_2); + assert_eq!(assigned_collators_at_session(3), None); + }); +} + +#[test] +fn assign_collators_insert_collator() { + new_test_ext().execute_with(|| { + MockData::mutate(|m| { + m.next_collator_assignment = AssignedCollators { + orchestrator_chain: vec![1, 2, 3, 4, 5], + container_chains: BTreeMap::from_iter(vec![ + (1001.into(), vec![6, 7]), + (1002.into(), vec![8, 9]), + ]), + }; + + m.nimbus_map = BTreeMap::from_iter( + vec![ + (1, "nmbs1"), + (2, "nmbs2"), + (3, "nmbs3"), + (4, "nmbs4"), + (5, "nmbs5"), + (6, "nmbs6"), + (7, "nmbs7"), + (8, "nmbs8"), + (9, "nmbs9"), + ] + .into_iter() + .map(|(id, nimbus_id)| (id, nimbus_id.to_string())), + ); + }); + + run_to_block(1); + + let expected_collators: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ("nmbs9", 1002), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + assert_eq!(assigned_collators_at_session(0), expected_collators); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), None); + + MockData::mutate(|m| { + m.nimbus_map.insert(10, "nmbs10".to_string()); + m.next_collator_assignment.orchestrator_chain.push(10); + }); + + run_to_session(1); + + let expected_collators_at_2: Option> = Some(BTreeMap::from_iter( + vec![ + ("nmbs1", 999), + ("nmbs2", 999), + ("nmbs3", 999), + ("nmbs4", 999), + ("nmbs5", 999), + ("nmbs6", 1001), + ("nmbs7", 1001), + ("nmbs8", 1002), + ("nmbs9", 1002), + ("nmbs10", 999), + ] + .into_iter() + .map(|(nimbus_id, para_id)| (nimbus_id.to_string(), para_id)), + )); + + // Collators in session 2 use the new keys, but collators in session 1 use the old keys + assert_eq!(assigned_collators_at_session(0), None); + assert_eq!(assigned_collators_at_session(1), expected_collators); + assert_eq!(assigned_collators_at_session(2), expected_collators_at_2); + assert_eq!(assigned_collators_at_session(3), None); + }); +} diff --git a/pallets/authority-mapping/Cargo.toml b/pallets/authority-mapping/Cargo.toml new file mode 100644 index 0000000..51224b8 --- /dev/null +++ b/pallets/authority-mapping/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "pallet-authority-mapping" +authors = { workspace = true } +description = "authority mapping pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +frame-support = { workspace = true } +frame-system = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive", "max-encoded-len" ] } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +sp-io = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "frame-support/std", + "frame-system/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/authority-mapping/src/lib.rs b/pallets/authority-mapping/src/lib.rs new file mode 100644 index 0000000..4158e5d --- /dev/null +++ b/pallets/authority-mapping/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Authority Mapping Pallet +//! +//! This pallet stores the AuthorityId -> AccountID mapping for two sessions +//! In particular it holds the mapping for the current and the past session +//! Both are necessary to verify block-authorship with respect to current +//! block proposals or blocks that have been proposed in the past-session + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +use { + frame_support::pallet_prelude::*, + sp_runtime::{ + traits::{AtLeast32BitUnsigned, CheckedSub}, + RuntimeAppPublic, + }, + sp_std::{collections::btree_map::BTreeMap, vec}, +}; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned; + + // Sessions after which keys should be removed + #[pallet::constant] + type SessionRemovalBoundary: Get; + + type AuthorityId: Member + + Parameter + + Ord + + RuntimeAppPublic + + MaybeSerializeDeserialize + + MaxEncodedLen; + } + + #[pallet::storage] + #[pallet::getter(fn authority_id_mapping)] + pub(super) type AuthorityIdMapping = StorageMap< + _, + Twox64Concat, + T::SessionIndex, + BTreeMap, + OptionQuery, + >; + + impl Pallet { + pub fn initializer_on_new_session( + session_index: &T::SessionIndex, + authorities: &[(T::AccountId, T::AuthorityId)], + ) { + // Remove only if the checked sub does not saturate + if let Some(session_index_to_remove) = + session_index.checked_sub(&T::SessionRemovalBoundary::get()) + { + AuthorityIdMapping::::remove(session_index_to_remove) + } + + let assignation: BTreeMap = + authorities.iter().cloned().map(|(a, b)| (b, a)).collect(); + AuthorityIdMapping::::insert(session_index, assignation); + } + } +} diff --git a/pallets/authority-mapping/src/mock.rs b/pallets/authority-mapping/src/mock.rs new file mode 100644 index 0000000..36f5389 --- /dev/null +++ b/pallets/authority-mapping/src/mock.rs @@ -0,0 +1,89 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate as pallet_authority_mapping, + frame_support::{ + parameter_types, + traits::{ConstU16, ConstU64}, + }, + frame_system as system, + sp_core::H256, + sp_runtime::{ + testing::UintAuthorityId, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, + }, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + AuthorityMapping: pallet_authority_mapping, + } +); + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = (); +} + +parameter_types! { + pub const SessionKeyRemovalLimit: u32 = 2u32; +} + +impl pallet_authority_mapping::Config for Test { + type SessionIndex = u32; + + type SessionRemovalBoundary = SessionKeyRemovalLimit; + + /// The identifier type for an authority. + type AuthorityId = UintAuthorityId; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} diff --git a/pallets/authority-mapping/src/tests.rs b/pallets/authority-mapping/src/tests.rs new file mode 100644 index 0000000..03a1b30 --- /dev/null +++ b/pallets/authority-mapping/src/tests.rs @@ -0,0 +1,69 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + super::*, + crate::mock::{new_test_ext, AuthorityMapping, Test}, +}; + +#[test] +fn session_0_fills_in_first_mapping() { + new_test_ext().execute_with(|| { + AuthorityMapping::initializer_on_new_session(&0, &[(1, 1u64.into())]); + + let v = AuthorityIdMapping::::get(0).unwrap(); + assert_eq!(v.len(), 1); + assert_eq!(v.get(&1u64.into()), Some(&1u64)); + }); +} + +#[test] +fn session_1_fills_in_second_mapping_but_does_not_remove_first() { + new_test_ext().execute_with(|| { + AuthorityMapping::initializer_on_new_session(&0, &[(1, 1u64.into())]); + + AuthorityMapping::initializer_on_new_session(&1, &[(1, 2u64.into())]); + + let v0 = AuthorityIdMapping::::get(0).unwrap(); + assert_eq!(v0.len(), 1); + assert_eq!(v0.get(&1u64.into()), Some(&1u64)); + + let v1 = AuthorityIdMapping::::get(1).unwrap(); + assert_eq!(v1.len(), 1); + assert_eq!(v1.get(&2u64.into()), Some(&1u64)); + }); +} + +#[test] +fn session_2_fills_in_third_mapping_removes_first_not_second() { + new_test_ext().execute_with(|| { + AuthorityMapping::initializer_on_new_session(&0, &[(1, 1u64.into())]); + + AuthorityMapping::initializer_on_new_session(&1, &[(1, 2u64.into())]); + + AuthorityMapping::initializer_on_new_session(&2, &[(1, 3u64.into())]); + + assert!(AuthorityIdMapping::::get(0).is_none()); + + let v1 = AuthorityIdMapping::::get(1).unwrap(); + assert_eq!(v1.len(), 1); + assert_eq!(v1.get(&2u64.into()), Some(&1u64)); + + let v2 = AuthorityIdMapping::::get(2).unwrap(); + assert_eq!(v2.len(), 1); + assert_eq!(v2.get(&3u64.into()), Some(&1u64)); + }); +} diff --git a/pallets/collator-assignment/Cargo.toml b/pallets/collator-assignment/Cargo.toml new file mode 100644 index 0000000..6d05552 --- /dev/null +++ b/pallets/collator-assignment/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "pallet-collator-assignment" +authors = { workspace = true } +description = "Collator assignment pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +dp-collator-assignment = { workspace = true } +frame-benchmarking = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive", "max-encoded-len" ] } +rand = { workspace = true } +rand_chacha = { workspace = true } +scale-info = { workspace = true } +serde = { workspace = true, optional = true, features = [ "derive" ] } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +tp-traits = { workspace = true } + +[dev-dependencies] +sp-io = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "dp-collator-assignment/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "parity-scale-codec/std", + "rand/std", + "rand_chacha/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-traits/std", + "tracing/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/collator-assignment/rpc/runtime-api/Cargo.toml b/pallets/collator-assignment/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000..aa03676 --- /dev/null +++ b/pallets/collator-assignment/rpc/runtime-api/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "pallet-collator-assignment-runtime-api" +authors = { workspace = true } +description = "Runtime API definition of pallet-collator-assignment" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-api = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "parity-scale-codec/std", + "scale-info/std", + "sp-api/std", +] diff --git a/pallets/collator-assignment/rpc/runtime-api/src/lib.rs b/pallets/collator-assignment/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000..7a584a8 --- /dev/null +++ b/pallets/collator-assignment/rpc/runtime-api/src/lib.rs @@ -0,0 +1,41 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Runtime API for CollatorAssignment pallet. Can be used by collators to check +//! which parachain will they be collating, as well as the current assignment of +//! collators to parachains and parachains to collators. + +#![cfg_attr(not(feature = "std"), no_std)] + +use scale_info::prelude::vec::Vec; + +sp_api::decl_runtime_apis! { + pub trait CollatorAssignmentApi where + AccountId: parity_scale_codec::Codec, + ParaId: parity_scale_codec::Codec, + { + /// Return the parachain that the given `AccountId` is collating for. + /// Returns `None` if the `AccountId` is not collating. + fn current_collator_parachain_assignment(account: AccountId) -> Option; + /// Return the parachain that the given `AccountId` will be collating for + /// in the next session change. + /// Returns `None` if the `AccountId` will not be collating. + fn future_collator_parachain_assignment(account: AccountId) -> Option; + /// Return the list of collators of the given `ParaId`. + /// Returns `None` if the `ParaId` is not in the registrar. + fn parachain_collators(para_id: ParaId) -> Option>; + } +} diff --git a/pallets/collator-assignment/src/assignment.rs b/pallets/collator-assignment/src/assignment.rs new file mode 100644 index 0000000..c3d8abe --- /dev/null +++ b/pallets/collator-assignment/src/assignment.rs @@ -0,0 +1,494 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + dp_collator_assignment::AssignedCollators, + sp_std::{ + cmp, + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + marker::PhantomData, + mem, + vec::Vec, + }, + tp_traits::{ParaId, RemoveInvulnerables as RemoveInvulnerablesT}, +}; + +// Separate import of `sp_std::vec!` macro, which cause issues with rustfmt if grouped +// with `sp_std::vec::Vec`. +use sp_std::vec; + +/// Helper methods to implement collator assignment algorithm +pub struct Assignment(PhantomData); + +impl Assignment +where + T: crate::Config, +{ + /// Recompute collator assignment from scratch. If the list of collators and the list of + /// container chains are shuffled, this returns a random assignment. + pub fn assign_collators_rotate_all( + collators: Vec, + orchestrator_chain: ChainNumCollators, + chains: Vec, + shuffle: Option, + ) -> Result, AssignmentError> + where + TShuffle: FnOnce(&mut Vec), + { + // This is just the "always_keep_old" algorithm but with an empty "old" + let old_assigned = Default::default(); + + Self::assign_collators_always_keep_old( + collators, + orchestrator_chain, + chains, + old_assigned, + shuffle, + ) + } + + /// Assign new collators to missing container_chains. + /// Old collators always have preference to remain on the same chain. + /// If there are no missing collators, nothing is changed. + /// + /// `chains` should be shuffled or at least rotated on every session to ensure + /// a fair distribution, because the order of that list affects container chain priority: + /// the first chain on that list will be the first one to get new collators. + /// + /// Similarly, in the `collators` list order means priority, the first collators will be more + /// likely to get assigned. Unlike the list of `chains` which should already be shuffled, + /// collators will be shuffled using the `shuffle` callback when needed. This allows the + /// algorithm to truncate the list of collators and only shuffle the first N. This ensures that + /// shuffling doesn't cause a collator with low priority to be assigned instead of a collator + /// with higher priority. + pub fn assign_collators_always_keep_old( + collators: Vec, + orchestrator_chain: ChainNumCollators, + mut chains: Vec, + mut old_assigned: AssignedCollators, + shuffle: Option, + ) -> Result, AssignmentError> + where + TShuffle: FnOnce(&mut Vec), + { + if collators.is_empty() { + return Err(AssignmentError::ZeroCollators); + } + // The rest of this function mostly treats orchestrator chain as another container chain, so move it into + // `old_assigned.container_chains` + let old_orchestrator_assigned = mem::take(&mut old_assigned.orchestrator_chain); + old_assigned + .container_chains + .insert(orchestrator_chain.para_id, old_orchestrator_assigned); + let mut old_assigned = old_assigned.container_chains; + // Orchestrator chain must be the first one in the list because it always has priority + chains.insert(0, orchestrator_chain); + let all_para_ids: Vec = chains.iter().map(|cc| cc.para_id).collect(); + let collators_set = BTreeSet::from_iter(collators.iter().cloned()); + let chains_with_collators = + Self::select_chains_with_collators(collators.len() as u32, &chains); + let chains_with_collators_set: BTreeSet = chains_with_collators + .iter() + .map(|(para_id, _num_collators)| *para_id) + .collect(); + Self::retain_valid_old_assigned( + &mut old_assigned, + &chains_with_collators_set, + &collators_set, + ); + + // Ensure the first `min_orchestrator_collators` of orchestrator chain are invulnerables + Self::prioritize_invulnerables(&collators, orchestrator_chain, &mut old_assigned); + + let new_assigned_chains = + Self::assign_full(collators, chains_with_collators, old_assigned, shuffle)?; + + let mut new_assigned = AssignedCollators { + container_chains: new_assigned_chains, + ..Default::default() + }; + + // Add container chains with 0 collators so that they are shown in UI + for para_id in all_para_ids { + new_assigned.container_chains.entry(para_id).or_default(); + } + + // The rest of this function mostly treats orchestrator chain as another container chain, remove it from + // container chains before returning the final assignment. + let orchestrator_assigned = new_assigned + .container_chains + .remove(&orchestrator_chain.para_id) + .unwrap(); + // Sanity check to avoid bricking orchestrator chain + if orchestrator_assigned.is_empty() { + return Err(AssignmentError::EmptyOrchestrator); + } + new_assigned.orchestrator_chain = orchestrator_assigned; + + Ok(new_assigned) + } + + /// Select which container chains will be assigned collators and how many collators, but do not specify which + /// collator goes to which chain. + /// + /// Each chain has a min and max number of collators. If the number of collators is not enough to reach the min, + /// no collators are assigned to that chain. + /// + /// If the available number of collators is: + /// * lower than the min of the first chain: we assign all the collators to the first chain. This is the + /// orchestrator chain and we always want it to have collators. + /// * lower than the sum of all the min: we cannot assign collators to all the chains. So remove chains until + /// we can. The order is important, the first chains will be assigned collators and the last ones will not. + /// * lower than the sum of all the max: we can assign the min value to all the chains, and have some leftover. + /// We use the same order to decide where this extra collators will go, by filling the max of the first chain, + /// then the max of the second chain, and so on. + /// * greater than the sum of all the max: all the chains will be assigned their max number of collators. + /// + /// # Params + /// + /// The first item of `chains` should be the orchestrator chain, because it will be the first one to be assigned + /// collators. + /// + /// # Returns + /// + /// A list of `(para_id, num_collators)`. + pub fn select_chains_with_collators( + num_collators: u32, + chains: &[ChainNumCollators], + ) -> Vec<(ParaId, u32)> { + if chains.is_empty() { + // Avoid panic if chains is empty + return vec![]; + } + // Let's count how many container chains we can support with the current number of collators + let mut available_collators = num_collators; + // Handle orchestrator chain in a special way, we always want to assign collators to it, even if we don't + // reach the min. + let min_orchestrator_collators = chains[0].min_collators; + available_collators = available_collators.saturating_sub(min_orchestrator_collators); + + let mut container_chains_with_collators = vec![chains[0]]; + // Skipping orchestrator chain because it was handled above + for cc in chains.iter().skip(1) { + if available_collators >= cc.min_collators { + available_collators -= cc.min_collators; + container_chains_with_collators.push(*cc); + } else if available_collators == 0 { + // Do not break if there are still some available collators. Even if they were not enough to reach the + // `min` of this chain, it is possible that one of the chains with less priority has a lower `min`, so + // that chain should be assigned collators. + break; + } + } + + let mut required_collators_min = 0; + for cc in &container_chains_with_collators { + required_collators_min += cc.min_collators; + } + + if num_collators < min_orchestrator_collators { + // Edge case: num collators less than min orchestrator collators: fill as much as we can + vec![(chains[0].para_id, num_collators)] + } else { + // After assigning the min to all the chains we have this remainder. The remainder will be assigned until + // all the chains reach the max value. + let mut required_collators_remainder = num_collators - required_collators_min; + let mut container_chains_variable = vec![]; + for cc in &container_chains_with_collators { + // Each chain will have `min + extra` collators, where extra is capped so `min + extra <= max`. + let extra = cmp::min( + required_collators_remainder, + cc.max_collators.saturating_sub(cc.min_collators), + ); + let num = cc.min_collators + extra; + required_collators_remainder -= extra; + container_chains_variable.push((cc.para_id, num)); + } + + container_chains_variable + } + } + + /// Same as `prioritize_invulnerables` but return the invulnerables instead of inserting them into `old_assigned`. + /// + /// Mutates `old_assigned` by removing invulnerables from their old chain, even if they will later be assigned to + /// the same chain. + pub fn remove_invulnerables( + collators: &[T::AccountId], + orchestrator_chain: ChainNumCollators, + old_assigned: &mut BTreeMap>, + ) -> Vec { + // TODO: clean this up, maybe change remove_invulnerables trait into something more ergonomic + let min_orchestrator_collators = orchestrator_chain.min_collators as usize; + let invulnerables_already_assigned = T::RemoveInvulnerables::remove_invulnerables( + &mut old_assigned + .get(&orchestrator_chain.para_id) + .cloned() + .unwrap_or_default(), + min_orchestrator_collators, + ); + let mut new_invulnerables = invulnerables_already_assigned; + if new_invulnerables.len() >= min_orchestrator_collators { + // We already had invulnerables, we will just move them to the front of the list if they weren't already + return new_invulnerables; + } + + // Not enough invulnerables currently assigned, get rest from new_collators + let mut new_collators = collators.to_vec(); + for (_id, cs) in old_assigned.iter() { + new_collators.retain(|c| !cs.contains(c)); + } + let num_missing_invulnerables = min_orchestrator_collators - new_invulnerables.len(); + let invulnerables_not_assigned = T::RemoveInvulnerables::remove_invulnerables( + &mut new_collators, + num_missing_invulnerables, + ); + new_invulnerables.extend(invulnerables_not_assigned); + + if new_invulnerables.len() >= min_orchestrator_collators { + // Got invulnerables from new_collators, and maybe some were already assigned + return new_invulnerables; + } + + // Still not enough invulnerables, try to get an invulnerable that is currently assigned somewhere else + let num_missing_invulnerables = min_orchestrator_collators - new_invulnerables.len(); + let mut collators = collators.to_vec(); + let new_invulnerables_set = BTreeSet::from_iter(new_invulnerables.iter().cloned()); + collators.retain(|c| { + // Remove collators already selected + !new_invulnerables_set.contains(c) + }); + let invulnerables_assigned_elsewhere = + T::RemoveInvulnerables::remove_invulnerables(&mut collators, num_missing_invulnerables); + + if invulnerables_assigned_elsewhere.is_empty() { + // If at this point we still do not have enough invulnerables, it means that there are no + // enough invulnerables, so no problem, but return the invulnerables + return new_invulnerables; + } + + new_invulnerables.extend(invulnerables_assigned_elsewhere.iter().cloned()); + + // In this case we must delete the old assignment of the invulnerables + let reassigned_invulnerables_set = BTreeSet::from_iter(invulnerables_assigned_elsewhere); + // old_assigned.remove_collators_in_set + for (_id, cs) in old_assigned.iter_mut() { + cs.retain(|c| !reassigned_invulnerables_set.contains(c)); + } + + new_invulnerables + } + + /// Ensure orchestrator chain has `min_orchestrator` invulnerables. If that's not possible, it tries to add as + /// many invulnerables as possible. + /// + /// Get invulnerables from: + /// * old_assigned in orchestrator + /// * new collators + /// * old_assigned elsewhere + /// + /// In that order. + /// + /// Mutates `old_assigned` because invulnerables will be inserted there, and if invulnerables were already + /// assigned to some other chain, they will be removed from that other chain as well. + /// + /// # Params + /// + /// * `old_assigned` must be a subset of `collators` + /// * `old_assigned` must not have duplicate collators. + /// + /// # Returns + /// + /// The number of invulnerables assigned to the orchestrator chain, capped to `min_collators`. + pub fn prioritize_invulnerables( + collators: &[T::AccountId], + orchestrator_chain: ChainNumCollators, + old_assigned: &mut BTreeMap>, + ) -> usize { + let new_invulnerables = + Self::remove_invulnerables(collators, orchestrator_chain, old_assigned); + + if !new_invulnerables.is_empty() { + Self::insert_invulnerables( + old_assigned.entry(orchestrator_chain.para_id).or_default(), + &new_invulnerables, + ); + } + + new_invulnerables.len() + } + + /// Assign collators assuming that the number of collators is greater than or equal to the required. + /// The order of both container chains and collators is important to ensure randomness when `old_assigned` is + /// empty. + /// + /// # Params + /// + /// * `old_assigned` does not need to be a subset of `collators`: collators are checked and removed. + /// * `old_assigned` does not need to be a subset of `chains`, unused para ids are removed. Collators + /// assigned to a para_id not present in `chains` may be reassigned to another para_id. + /// * `chains` `num_collators` can be 0. In that case an empty vec is returned for that para id. + /// * `old_assigned` must not have duplicate collators. + /// * `shuffle` is used to shuffle the list collators. The list will be truncated to only have + /// the number of required collators, to ensure that shuffling doesn't cause a collator with low + /// priority to be assigned instead of a collator with higher priority. + /// + /// # Returns + /// + /// The collator assigment, a map from `ParaId` to `Vec`. + /// + /// Or an error if the number of collators is not enough to fill all the chains, or if the required number + /// of collators overflows a `u32`. + pub fn assign_full( + collators: Vec, + chains: Vec<(ParaId, u32)>, + mut old_assigned: BTreeMap>, + shuffle: Option, + ) -> Result>, AssignmentError> + where + TShuffle: FnOnce(&mut Vec), + { + let mut required_collators = 0usize; + for (_para_id, num_collators) in chains.iter() { + let num_collators = + usize::try_from(*num_collators).map_err(|_| AssignmentError::NotEnoughCollators)?; + required_collators = required_collators + .checked_add(num_collators) + .ok_or(AssignmentError::NotEnoughCollators)?; + } + + // This check is necessary to ensure priority: if the number of collators is less than required, it is + // possible that the chain with the least priority could be assigned collators (since they are in + // old_assigned), while some chains with higher priority might have no collators. + if collators.len() < required_collators { + return Err(AssignmentError::NotEnoughCollators); + } + // We checked that the sum of all `num_collators` fits in `usize`, so we can safely use `as usize`. + + // Remove invalid collators and para ids from `old_assigned` + let para_ids_set = + BTreeSet::from_iter(chains.iter().map(|(para_id, _num_collators)| *para_id)); + let collators_set = BTreeSet::from_iter(collators.iter().cloned()); + Self::retain_valid_old_assigned(&mut old_assigned, ¶_ids_set, &collators_set); + + // Truncate num collators to required + for (para_id, num_collators) in chains.iter() { + let entry = old_assigned.entry(*para_id).or_default(); + entry.truncate(*num_collators as usize); + } + + // Count number of needed new collators. This is equivalent to: + // `required_collators - old_assigned.iter().map(|cs| cs.len()).sum()`. + let mut needed_new_collators = 0; + for (para_id, num_collators) in chains.iter() { + let cs = old_assigned.entry(*para_id).or_default(); + needed_new_collators += (*num_collators as usize).saturating_sub(cs.len()); + } + + let assigned_collators: BTreeSet = old_assigned + .iter() + .flat_map(|(_para_id, para_collators)| para_collators.iter().cloned()) + .collect(); + + // Truncate list of new_collators to `needed_new_collators` and shuffle it. + // This has the effect of keeping collator priority (the first collator of that list is more + // likely to be assigned to a chain than the last collator of that list), while also + // ensuring randomness (the original order does not directly affect which chain the + // collators are assigned to). + let mut new_collators: Vec<_> = collators + .into_iter() + .filter(|x| { + // Keep collators not already assigned + !assigned_collators.contains(x) + }) + .take(needed_new_collators) + .collect(); + if let Some(shuffle) = shuffle { + shuffle(&mut new_collators); + } + let mut new_collators = new_collators.into_iter(); + + // Fill missing collators + for (para_id, num_collators) in chains.iter() { + let cs = old_assigned.entry(*para_id).or_default(); + + while cs.len() < *num_collators as usize { + // This error should never happen because we calculated `needed_new_collators` + // using the same algorithm + let nc = new_collators + .next() + .ok_or(AssignmentError::NotEnoughCollators)?; + cs.push(nc); + } + } + + Ok(old_assigned) + } + + /// Insert invulnerables ensuring that they are always the first in the list. + /// The order of both lists is preserved. + /// `assigned` may already contain the invulnerables, in that case they are only moved to the front. + /// + /// Invulnerables need to be the first of the list because we may truncate the list of collators if the number of + /// collators changes, and in that case we want invulnerables to stay assigned there. + pub fn insert_invulnerables(assigned: &mut Vec, invulnerables: &[T::AccountId]) { + assigned.retain(|item| !invulnerables.contains(item)); + + let mut new_assigned = invulnerables.to_vec(); + new_assigned.extend(mem::take(assigned)); + + *assigned = new_assigned; + } + + /// Removes invalid entries from `old_assigned`: + /// + /// * para ids not in `chains_with_collators` + /// * collators not in `collators` + pub fn retain_valid_old_assigned( + old_assigned: &mut BTreeMap>, + chains_with_collators: &BTreeSet, + collators: &BTreeSet, + ) { + // old_assigned.remove_container_chains_not_in_set + old_assigned.retain(|id, _cs| chains_with_collators.contains(id)); + // old_assigned.remove_collators_not_in_set + for (_id, cs) in old_assigned.iter_mut() { + cs.retain(|c| collators.contains(c)); + } + } +} + +/// Errors than can happen during collator assignment +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AssignmentError { + /// An empty list of collators was passed to `assign_collators_always_keep_old` + ZeroCollators, + /// The required number of collators for `assign_full` is greater than the provided number of collators. + /// Also includes possible overflows in number of collators. + NotEnoughCollators, + /// No collators were assigned to orchestrator chain + EmptyOrchestrator, +} + +/// A `ParaId` and a range of collators that need to be assigned to it. +/// This can be a container chain, a parathread, or the orchestrator chain. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ChainNumCollators { + pub para_id: ParaId, + pub min_collators: u32, + // This will only be filled if all the other min have been reached + pub max_collators: u32, +} diff --git a/pallets/collator-assignment/src/benchmarking.rs b/pallets/collator-assignment/src/benchmarking.rs new file mode 100644 index 0000000..3078806 --- /dev/null +++ b/pallets/collator-assignment/src/benchmarking.rs @@ -0,0 +1,123 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Benchmarking setup for pallet-invulnerables + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +#[allow(unused)] +use { + crate::Pallet, + frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}, + frame_support::{ + pallet_prelude::*, + traits::{Currency, EnsureOrigin, Get}, + }, + frame_system::{EventRecord, RawOrigin}, + sp_std::collections::btree_map::BTreeMap, + sp_std::prelude::*, +}; + +const SEED: u32 = 0; + +fn invulnerable(c: u32, seed: u32) -> T::AccountId { + account::("candidate", c, seed) +} + +fn invulnerables(count: u32, seed: u32) -> Vec { + (0..count) + .map(|c| invulnerable::(c, seed)) + .collect::>() +} + +fn assert_event_is_present(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let event_records: Vec<::RuntimeEvent> = + events.iter().map(|i| i.event.clone()).collect(); + assert!(event_records.contains(&system_event)); +} + +#[benchmarks] +mod benchmarks { + use super::*; + + // worst case for new session. + // TODO: this should be parametric over the config values: + // * min_collators_for_orchestrator + // * max_collators_for_orchestrator + // * collators_per_container + #[benchmark] + fn new_session(x: Linear<1, 200>, y: Linear<1, 20>) -> Result<(), BenchmarkError> { + frame_system::Pallet::::set_block_number(0u32.into()); + + let collators = invulnerables::(x, SEED); + let container_chains: Vec<_> = (0..y).map(ParaId::from).collect(); + let session_index = 0u32.into(); + T::ContainerChains::set_session_container_chains(session_index, &container_chains); + T::RemoveParaIdsWithNoCredits::make_valid_para_ids(&container_chains); + T::HostConfiguration::set_host_configuration(session_index); + + // Assign random collators to test worst case: when collators need to be checked against existing collators + // In this case all of the old collators don't exist anymore + let old_container_chains: Vec<(ParaId, _)> = (0..y) + .map(|para_id| (para_id.into(), invulnerables::(10, SEED + 2 + para_id))) + .collect(); + + let old_assigned = AssignedCollators { + orchestrator_chain: invulnerables::(100, SEED + 1), + container_chains: BTreeMap::from_iter(old_container_chains), + }; + >::put(&old_assigned); + // Do not use [0; 32] because that seed will not shuffle the list of collators + // We use a different random seed every time to make sure that the event is included + let random_seed = [x as u8; 32]; + >::put(random_seed); + + #[block] + { + >::initializer_on_new_session(&session_index, collators); + } + + // Assignment changed + assert_ne!(>::get(), old_assigned); + // New assignment is not empty + // If more than one, at least one chain should have gotten collators + if x > 1 { + assert_ne!( + >::get().container_chains.len(), + 0 + ); + } + + // Worst case is `full_rotation: false` because it needs to check the previous assignment + assert_event_is_present::( + Event::NewPendingAssignment { + random_seed, + full_rotation: false, + target_session: T::SessionIndex::from(1u32), + } + .into(), + ); + + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test,); +} diff --git a/pallets/collator-assignment/src/lib.rs b/pallets/collator-assignment/src/lib.rs new file mode 100644 index 0000000..1b4d5fa --- /dev/null +++ b/pallets/collator-assignment/src/lib.rs @@ -0,0 +1,533 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Collator Assignment Pallet +//! +//! This pallet assigns a list of collators to: +//! - the orchestrator chain +//! - a set of container chains +//! +//! The set of container chains is retrieved thanks to the GetContainerChains trait +//! The number of collators to assign to the orchestrator chain and the number +//! of collators to assign to each container chain is retrieved through the GetHostConfiguration +//! trait. +//! +//! The pallet uses the following approach: +//! +//! - First, it aims at filling the necessary collators to serve the orchestrator chain +//! - Second, it aims at filling in-order (FIFO) the existing containerChains +//! +//! Upon new session, this pallet takes whatever assignation was in the PendingCollatorContainerChain +//! storage, and assigns it as the current CollatorContainerChain. In addition, it takes the next +//! queued set of parachains and collators and calculates the assignment for the next session, storing +//! it in the PendingCollatorContainerChain storage item. +//! +//! The reason for the collator-assignment pallet to work with a one-session delay assignment is because +//! we want collators to know at least one session in advance the container chain/orchestrator that they +//! are assigned to. + +#![cfg_attr(not(feature = "std"), no_std)] + +use { + crate::assignment::{Assignment, ChainNumCollators}, + frame_support::{pallet_prelude::*, traits::Currency}, + frame_system::pallet_prelude::BlockNumberFor, + rand::{seq::SliceRandom, SeedableRng}, + rand_chacha::ChaCha20Rng, + sp_runtime::{ + traits::{AtLeast32BitUnsigned, One, Zero}, + Saturating, + }, + sp_std::{collections::btree_set::BTreeSet, fmt::Debug, prelude::*, vec}, + tp_traits::{ + CollatorAssignmentHook, CollatorAssignmentTip, GetContainerChainAuthor, + GetHostConfiguration, GetSessionContainerChains, ParaId, RemoveInvulnerables, + RemoveParaIdsWithNoCredits, ShouldRotateAllCollators, Slot, + }, +}; +pub use {dp_collator_assignment::AssignedCollators, pallet::*}; + +mod assignment; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::WeightInfo; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type SessionIndex: parity_scale_codec::FullCodec + + TypeInfo + + Copy + + AtLeast32BitUnsigned + + Debug; + // `SESSION_DELAY` is used to delay any changes to Paras registration or configurations. + // Wait until the session index is 2 larger then the current index to apply any changes, + // which guarantees that at least one full session has passed before any changes are applied. + type HostConfiguration: GetHostConfiguration; + type ContainerChains: GetSessionContainerChains; + type SelfParaId: Get; + type ShouldRotateAllCollators: ShouldRotateAllCollators; + type GetRandomnessForNextBlock: GetRandomnessForNextBlock>; + type RemoveInvulnerables: RemoveInvulnerables; + type RemoveParaIdsWithNoCredits: RemoveParaIdsWithNoCredits; + type CollatorAssignmentHook: CollatorAssignmentHook>; + type Currency: Currency; + type CollatorAssignmentTip: CollatorAssignmentTip>; + /// The weight information of this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + NewPendingAssignment { + random_seed: [u8; 32], + full_rotation: bool, + target_session: T::SessionIndex, + }, + } + + #[pallet::storage] + #[pallet::getter(fn collator_container_chain)] + pub(crate) type CollatorContainerChain = + StorageValue<_, AssignedCollators, ValueQuery>; + + /// Pending configuration changes. + /// + /// This is a list of configuration changes, each with a session index at which it should + /// be applied. + /// + /// The list is sorted ascending by session index. Also, this list can only contain at most + /// 2 items: for the next session and for the `scheduled_session`. + #[pallet::storage] + #[pallet::getter(fn pending_collator_container_chain)] + pub(crate) type PendingCollatorContainerChain = + StorageValue<_, Option>, ValueQuery>; + + /// Randomness from previous block. Used to shuffle collators on session change. + /// Should only be set on the last block of each session and should be killed on the on_initialize of the next block. + /// The default value of [0; 32] disables randomness in the pallet. + #[pallet::storage] + #[pallet::getter(fn randomness)] + pub(crate) type Randomness = StorageValue<_, [u8; 32], ValueQuery>; + + #[pallet::call] + impl Pallet {} + + /// A struct that holds the assignment that is active after the session change and optionally + /// the assignment that becomes active after the next session change. + pub struct SessionChangeOutcome { + /// New active assignment. + pub active_assignment: AssignedCollators, + /// Next session active assignment. + pub next_assignment: AssignedCollators, + /// Total number of registered parachains before filtering them out, used as a weight hint + pub num_total_registered_paras: u32, + } + + impl Pallet { + /// Assign new collators + /// collators should be queued collators + pub fn assign_collators( + current_session_index: &T::SessionIndex, + random_seed: [u8; 32], + collators: Vec, + ) -> SessionChangeOutcome { + // We work with one session delay to calculate assignments + let session_delay = T::SessionIndex::one(); + let target_session_index = current_session_index.saturating_add(session_delay); + // We get the containerChains that we will have at the target session + let container_chains = + T::ContainerChains::session_container_chains(target_session_index); + let num_total_registered_paras = + (container_chains.parachains.len() + container_chains.parathreads.len()) as u32; + let mut container_chain_ids = container_chains.parachains; + let mut parathreads: Vec<_> = container_chains + .parathreads + .into_iter() + .map(|(para_id, _)| para_id) + .collect(); + + // We read current assigned collators + let old_assigned = Self::read_assigned_collators(); + let old_assigned_para_ids: BTreeSet = + old_assigned.container_chains.keys().cloned().collect(); + + // Remove the containerChains that do not have enough credits for block production + T::RemoveParaIdsWithNoCredits::remove_para_ids_with_no_credits( + &mut container_chain_ids, + &old_assigned_para_ids, + ); + // TODO: parathreads should be treated a bit differently, they don't need to have the same amount of credits + // as parathreads because they will not be producing blocks on every slot. + T::RemoveParaIdsWithNoCredits::remove_para_ids_with_no_credits( + &mut parathreads, + &old_assigned_para_ids, + ); + + let mut shuffle_collators = None; + // If the random_seed is all zeros, we don't shuffle the list of collators nor the list + // of container chains. + // This should only happen in tests, and in the genesis block. + if random_seed != [0; 32] { + let mut rng: ChaCha20Rng = SeedableRng::from_seed(random_seed); + container_chain_ids.shuffle(&mut rng); + parathreads.shuffle(&mut rng); + shuffle_collators = Some(move |collators: &mut Vec| { + collators.shuffle(&mut rng); + }) + } + + let orchestrator_chain = ChainNumCollators { + para_id: T::SelfParaId::get(), + min_collators: T::HostConfiguration::min_collators_for_orchestrator( + target_session_index, + ), + max_collators: T::HostConfiguration::max_collators_for_orchestrator( + target_session_index, + ), + }; + // Initialize list of chains as `[container1, container2, parathread1, parathread2]`. + // The order means priority: the first chain in the list will be the first one to get assigned collators. + // Chains will not be assigned less than `min_collators`, except the orchestrator chain. + // First all chains will be assigned `min_collators`, and then the first one will be assigned up to `max`, + // then the second one, and so on. + let mut chains = vec![]; + let collators_per_container = + T::HostConfiguration::collators_per_container(target_session_index); + for para_id in &container_chain_ids { + chains.push(ChainNumCollators { + para_id: *para_id, + min_collators: collators_per_container, + max_collators: collators_per_container, + }); + } + let collators_per_parathread = + T::HostConfiguration::collators_per_parathread(target_session_index); + for para_id in ¶threads { + chains.push(ChainNumCollators { + para_id: *para_id, + min_collators: collators_per_parathread, + max_collators: collators_per_parathread, + }); + } + + // Are there enough collators to satisfy the minimum demand? + let enough_collators_for_all_chain = collators.len() as u32 + >= T::HostConfiguration::min_collators_for_orchestrator(target_session_index) + .saturating_add( + collators_per_container.saturating_mul(container_chain_ids.len() as u32), + ) + .saturating_add( + collators_per_parathread.saturating_mul(parathreads.len() as u32), + ); + + // Prioritize paras by tip on congestion + // As of now this doesn't distinguish between parachains and parathreads + // TODO apply different logic to parathreads + if !enough_collators_for_all_chain { + chains.sort_by(|a, b| { + T::CollatorAssignmentTip::get_para_tip(b.para_id) + .cmp(&T::CollatorAssignmentTip::get_para_tip(a.para_id)) + }); + } + + // We assign new collators + // we use the config scheduled at the target_session_index + let new_assigned = + if T::ShouldRotateAllCollators::should_rotate_all_collators(target_session_index) { + log::debug!( + "Collator assignment: rotating collators. Session {:?}, Seed: {:?}", + current_session_index.encode(), + random_seed + ); + + Self::deposit_event(Event::NewPendingAssignment { + random_seed, + full_rotation: true, + target_session: target_session_index, + }); + + Assignment::::assign_collators_rotate_all( + collators, + orchestrator_chain, + chains, + shuffle_collators, + ) + } else { + log::debug!( + "Collator assignment: keep old assigned. Session {:?}, Seed: {:?}", + current_session_index.encode(), + random_seed + ); + + Self::deposit_event(Event::NewPendingAssignment { + random_seed, + full_rotation: false, + target_session: target_session_index, + }); + + Assignment::::assign_collators_always_keep_old( + collators, + orchestrator_chain, + chains, + old_assigned.clone(), + shuffle_collators, + ) + }; + + let mut new_assigned = match new_assigned { + Ok(x) => x, + Err(e) => { + log::error!( + "Error in collator assignment, will keep previous assignment. {:?}", + e + ); + + old_assigned.clone() + } + }; + + let mut assigned_containers = new_assigned.container_chains.clone(); + assigned_containers.retain(|_, v| !v.is_empty()); + + // On congestion, prioritized chains need to pay the minimum tip of the prioritized chains + let maybe_tip: Option> = if enough_collators_for_all_chain { + None + } else { + assigned_containers + .into_keys() + .filter_map(T::CollatorAssignmentTip::get_para_tip) + .min() + }; + + // TODO: this probably is asking for a refactor + // only apply the onCollatorAssignedHook if sufficient collators + for para_id in &container_chain_ids { + if !new_assigned + .container_chains + .get(para_id) + .unwrap_or(&vec![]) + .is_empty() + { + if let Err(e) = T::CollatorAssignmentHook::on_collators_assigned( + *para_id, + maybe_tip.as_ref(), + false, + ) { + // On error remove para from assignment + log::warn!( + "CollatorAssignmentHook error! Removing para {} from assignment: {:?}", + u32::from(*para_id), + e + ); + new_assigned.container_chains.remove(para_id); + } + } + } + + for para_id in ¶threads { + if !new_assigned + .container_chains + .get(para_id) + .unwrap_or(&vec![]) + .is_empty() + { + if let Err(e) = T::CollatorAssignmentHook::on_collators_assigned( + *para_id, + maybe_tip.as_ref(), + true, + ) { + // On error remove para from assignment + log::warn!( + "CollatorAssignmentHook error! Removing para {} from assignment: {:?}", + u32::from(*para_id), + e + ); + new_assigned.container_chains.remove(para_id); + } + } + } + + let mut pending = PendingCollatorContainerChain::::get(); + + let old_assigned_changed = old_assigned != new_assigned; + let mut pending_changed = false; + // Update CollatorContainerChain using last entry of pending, if needed + if let Some(current) = pending.take() { + pending_changed = true; + CollatorContainerChain::::put(current); + } + if old_assigned_changed { + pending = Some(new_assigned.clone()); + pending_changed = true; + } + // Update PendingCollatorContainerChain, if it changed + if pending_changed { + PendingCollatorContainerChain::::put(pending); + } + + // Only applies to session index 0 + if current_session_index == &T::SessionIndex::zero() { + CollatorContainerChain::::put(new_assigned.clone()); + return SessionChangeOutcome { + active_assignment: new_assigned.clone(), + next_assignment: new_assigned, + num_total_registered_paras, + }; + } + + SessionChangeOutcome { + active_assignment: old_assigned, + next_assignment: new_assigned, + num_total_registered_paras, + } + } + + // Returns the assigned collators as read from storage. + // If there is any item in PendingCollatorContainerChain, returns that element. + // Otherwise, reads and returns the current CollatorContainerChain + fn read_assigned_collators() -> AssignedCollators { + let mut pending_collator_list = PendingCollatorContainerChain::::get(); + + if let Some(assigned_collators) = pending_collator_list.take() { + assigned_collators + } else { + // Read current + CollatorContainerChain::::get() + } + } + + pub fn initializer_on_new_session( + session_index: &T::SessionIndex, + collators: Vec, + ) -> SessionChangeOutcome { + let random_seed = Randomness::::take(); + let num_collators = collators.len(); + let assigned_collators = Self::assign_collators(session_index, random_seed, collators); + let num_total_registered_paras = assigned_collators.num_total_registered_paras; + + frame_system::Pallet::::register_extra_weight_unchecked( + T::WeightInfo::new_session(num_collators as u32, num_total_registered_paras), + DispatchClass::Mandatory, + ); + + assigned_collators + } + } + + impl GetContainerChainAuthor for Pallet { + // TODO: pending collator container chain if the block is a session change! + fn author_for_slot(slot: Slot, para_id: ParaId) -> Option { + let assigned_collators = Pallet::::collator_container_chain(); + let collators = if para_id == T::SelfParaId::get() { + Some(&assigned_collators.orchestrator_chain) + } else { + assigned_collators.container_chains.get(¶_id) + }?; + + if collators.is_empty() { + // Avoid division by zero below + return None; + } + let author_index = u64::from(slot) % collators.len() as u64; + collators.get(author_index as usize).cloned() + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_authors_for_para_id(para_id: ParaId, authors: Vec) { + let mut assigned_collators = Pallet::::collator_container_chain(); + assigned_collators.container_chains.insert(para_id, authors); + CollatorContainerChain::::put(assigned_collators); + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(n: BlockNumberFor) -> Weight { + let mut weight = Weight::zero(); + + // Account reads and writes for on_finalize + if T::GetRandomnessForNextBlock::should_end_session(n.saturating_add(One::one())) { + weight += T::DbWeight::get().reads_writes(1, 1); + } + + weight + } + + fn on_finalize(n: BlockNumberFor) { + // If the next block is a session change, read randomness and store in pallet storage + if T::GetRandomnessForNextBlock::should_end_session(n.saturating_add(One::one())) { + let random_seed = T::GetRandomnessForNextBlock::get_randomness(); + Randomness::::put(random_seed); + } + } + } +} + +/// Balance used by this pallet +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +pub struct RotateCollatorsEveryNSessions(PhantomData); + +impl ShouldRotateAllCollators for RotateCollatorsEveryNSessions +where + Period: Get, +{ + fn should_rotate_all_collators(session_index: u32) -> bool { + let period = Period::get(); + + if period == 0 { + // A period of 0 disables rotation + false + } else { + session_index % Period::get() == 0 + } + } +} + +pub trait GetRandomnessForNextBlock { + fn should_end_session(block_number: BlockNumber) -> bool; + fn get_randomness() -> [u8; 32]; +} + +impl GetRandomnessForNextBlock for () { + fn should_end_session(_block_number: BlockNumber) -> bool { + false + } + + fn get_randomness() -> [u8; 32] { + [0; 32] + } +} diff --git a/pallets/collator-assignment/src/mock.rs b/pallets/collator-assignment/src/mock.rs new file mode 100644 index 0000000..b42086e --- /dev/null +++ b/pallets/collator-assignment/src/mock.rs @@ -0,0 +1,400 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + self as pallet_collator_assignment, pallet::CollatorContainerChain, + GetRandomnessForNextBlock, RotateCollatorsEveryNSessions, + }, + frame_support::{ + parameter_types, + traits::{ConstU16, ConstU64, Hooks}, + weights::Weight, + }, + frame_system as system, + parity_scale_codec::{Decode, Encode}, + sp_core::{Get, H256}, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, + }, + sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + tp_traits::{ + CollatorAssignmentHook, CollatorAssignmentTip, ParaId, ParathreadParams, + RemoveInvulnerables, RemoveParaIdsWithNoCredits, SessionContainerChains, + }, + tracing_subscriber::{layer::SubscriberExt, FmtSubscriber}, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + MockData: mock_data, + CollatorAssignment: pallet_collator_assignment, + } +); + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = (); +} + +// Pallet to provide some mock data, used to test +#[frame_support::pallet] +pub mod mock_data { + use {super::*, frame_support::pallet_prelude::*}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn mock)] + pub(super) type Mock = StorageValue<_, Mocks, ValueQuery>; + + impl Pallet { + pub fn get() -> Mocks { + Mock::::get() + } + pub fn mutate(f: F) -> R + where + F: FnOnce(&mut Mocks) -> R, + { + Mock::::mutate(f) + } + } +} + +#[derive( + Default, Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo, +)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Mocks { + pub min_orchestrator_chain_collators: u32, + pub max_orchestrator_chain_collators: u32, + pub collators_per_container: u32, + pub collators_per_parathread: u32, + pub collators: Vec, + pub container_chains: Vec, + pub parathreads: Vec, + pub random_seed: [u8; 32], + // None means 5 + pub full_rotation_period: Option, + pub apply_tip: bool, + pub assignment_hook_errors: bool, +} + +impl mock_data::Config for Test {} + +// In tests, we ignore the session_index param, so changes to the configuration are instant + +pub struct HostConfigurationGetter; + +parameter_types! { + pub const ParachainId: ParaId = ParaId::new(1000); +} + +impl pallet_collator_assignment::GetHostConfiguration for HostConfigurationGetter { + fn max_collators(_session_index: u32) -> u32 { + unimplemented!() + } + + fn min_collators_for_orchestrator(_session_index: u32) -> u32 { + MockData::mock().min_orchestrator_chain_collators + } + + fn max_collators_for_orchestrator(_session_index: u32) -> u32 { + MockData::mock().max_orchestrator_chain_collators + } + + fn collators_per_container(_session_index: u32) -> u32 { + MockData::mock().collators_per_container + } + + fn collators_per_parathread(_session_index: u32) -> u32 { + MockData::mock().collators_per_parathread + } + #[cfg(feature = "runtime-benchmarks")] + fn set_host_configuration(_session_index: u32) { + MockData::mutate(|mocks| { + mocks.collators = vec![100]; + mocks.min_orchestrator_chain_collators = 1; + mocks.collators_per_container = 1; + mocks.max_orchestrator_chain_collators = 1; + }) + } +} + +pub struct CollatorsGetter; + +impl GetCollators for CollatorsGetter { + fn collators(_session_index: u32) -> Vec { + MockData::mock().collators + } +} + +pub struct ContainerChainsGetter; + +impl tp_traits::GetSessionContainerChains for ContainerChainsGetter { + fn session_container_chains(_session_index: u32) -> SessionContainerChains { + let parachains = MockData::mock() + .container_chains + .iter() + .cloned() + .map(ParaId::from) + .collect(); + + let parathreads = MockData::mock() + .parathreads + .iter() + .cloned() + .map(|para_id| { + ( + ParaId::from(para_id), + ParathreadParams { + slot_frequency: Default::default(), + }, + ) + }) + .collect(); + + SessionContainerChains { + parachains, + parathreads, + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_session_container_chains(_session_index: u32, para_ids: &[ParaId]) { + MockData::mutate(|mocks| { + mocks.container_chains = para_ids.iter().cloned().map(|x| x.into()).collect(); + }); + } +} + +pub struct MockGetRandomnessForNextBlock; + +impl GetRandomnessForNextBlock for MockGetRandomnessForNextBlock { + fn should_end_session(n: u64) -> bool { + n % 5 == 0 + } + + fn get_randomness() -> [u8; 32] { + MockData::mock().random_seed + } +} + +parameter_types! { + pub const CollatorRotationSessionPeriod: u32 = 5; +} + +pub struct MockCollatorRotationSessionPeriod; + +impl Get for MockCollatorRotationSessionPeriod { + fn get() -> u32 { + MockData::mock().full_rotation_period.unwrap_or(5) + } +} + +// Mock the service payment tip as only for 1003 +pub struct MockCollatorAssignmentTip; + +impl CollatorAssignmentTip for MockCollatorAssignmentTip { + fn get_para_tip(para_id: ParaId) -> Option { + if MockData::mock().apply_tip && (para_id == 1003u32.into() || para_id == 1004u32.into()) { + Some(1_000u32) + } else { + None + } + } +} +pub struct MockCollatorAssignmentHook; + +impl CollatorAssignmentHook for MockCollatorAssignmentHook { + fn on_collators_assigned( + para_id: ParaId, + _maybe_tip: Option<&u32>, + _is_parathread: bool, + ) -> Result { + // Only fail for para 1001 + if MockData::mock().assignment_hook_errors && para_id == 1001.into() { + // The error doesn't matter + Err(sp_runtime::DispatchError::Unavailable) + } else { + Ok(Weight::default()) + } + } +} + +impl pallet_collator_assignment::Config for Test { + type RuntimeEvent = RuntimeEvent; + type SessionIndex = u32; + type HostConfiguration = HostConfigurationGetter; + type ContainerChains = ContainerChainsGetter; + type SelfParaId = ParachainId; + type ShouldRotateAllCollators = + RotateCollatorsEveryNSessions; + type GetRandomnessForNextBlock = MockGetRandomnessForNextBlock; + type RemoveInvulnerables = RemoveAccountIdsAbove100; + type RemoveParaIdsWithNoCredits = RemoveParaIdsAbove5000; + type CollatorAssignmentHook = MockCollatorAssignmentHook; + type CollatorAssignmentTip = MockCollatorAssignmentTip; + type Currency = (); + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut ext: sp_io::TestExternalities = system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + + ext.execute_with(|| { + MockData::mutate(|mocks| { + // Initialize collators with 1 collator to avoid error `ZeroCollators` in session 0 + mocks.collators = vec![100]; + mocks.min_orchestrator_chain_collators = 1; + }) + }); + + ext +} + +pub trait GetCollators { + fn collators(session_index: SessionIndex) -> Vec; +} + +pub fn run_to_block(n: u64) { + let old_block_number = System::block_number(); + let session_len = 5; + + for x in (old_block_number + 1)..=n { + System::reset_events(); + System::set_block_number(x); + CollatorAssignment::on_initialize(x); + + if x % session_len == 1 { + let session_index = (x / session_len) as u32; + CollatorAssignment::initializer_on_new_session( + &session_index, + CollatorsGetter::collators(session_index), + ); + } + + CollatorAssignment::on_finalize(x); + } +} + +/// Any AccountId >= 100 will be considered an invulnerable +pub struct RemoveAccountIdsAbove100; + +impl RemoveInvulnerables for RemoveAccountIdsAbove100 { + fn remove_invulnerables(collators: &mut Vec, num_invulnerables: usize) -> Vec { + let mut invulnerables = vec![]; + collators.retain(|x| { + if invulnerables.len() < num_invulnerables && *x >= 100 { + invulnerables.push(*x); + false + } else { + true + } + }); + + invulnerables + } +} + +/// Any ParaId >= 5000 will be considered to not have enough credits +pub struct RemoveParaIdsAbove5000; + +impl RemoveParaIdsWithNoCredits for RemoveParaIdsAbove5000 { + fn remove_para_ids_with_no_credits( + para_ids: &mut Vec, + _currently_assigned: &BTreeSet, + ) { + para_ids.retain(|para_id| *para_id <= ParaId::from(5000)); + } + + #[cfg(feature = "runtime-benchmarks")] + fn make_valid_para_ids(_para_ids: &[ParaId]) {} +} + +/// Returns a map of collator to assigned para id +pub fn assigned_collators() -> BTreeMap { + let assigned_collators = CollatorContainerChain::::get(); + + let mut h = BTreeMap::new(); + + for (para_id, collators) in assigned_collators.container_chains.iter() { + for collator in collators.iter() { + h.insert(*collator, u32::from(*para_id)); + } + } + + for collator in assigned_collators.orchestrator_chain { + h.insert(collator, 1000); + } + + h +} + +/// Returns the default assignment for session 0 used in tests. Collator 100 is assigned to the orchestrator chain. +pub fn initial_collators() -> BTreeMap { + BTreeMap::from_iter(vec![(100, 1000)]) +} + +/// Executes code without printing any logs. Can be used in tests where we expect logs to be printed, to avoid clogging +/// up stderr. Only affects the current thread, if `f` spawns any threads or if logs come from another thread, they will +/// not be silenced. +pub fn silence_logs R, R>(f: F) -> R { + let no_logging_layer = tracing_subscriber::filter::LevelFilter::OFF; + let no_logging_subscriber = FmtSubscriber::builder().finish().with(no_logging_layer); + + tracing::subscriber::with_default(no_logging_subscriber, f) +} diff --git a/pallets/collator-assignment/src/tests.rs b/pallets/collator-assignment/src/tests.rs new file mode 100644 index 0000000..dedcb9c --- /dev/null +++ b/pallets/collator-assignment/src/tests.rs @@ -0,0 +1,1429 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{mock::*, CollatorContainerChain, Event, PendingCollatorContainerChain}, + dp_collator_assignment::AssignedCollators, + std::collections::BTreeMap, +}; + +mod assign_full; +mod prioritize_invulnerables; +mod select_chains; + +#[test] +fn assign_initial_collators() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + m.container_chains = vec![1001, 1002] + }); + + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(6); + + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + }); +} + +#[test] +fn assign_collators_after_one_leaves_container() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + m.container_chains = vec![1001, 1002] + }); + + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(6); + + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + + MockData::mutate(|m| { + // Remove 6 + m.collators = vec![1, 2, 3, 4, 5, /*6,*/ 7, 8, 9, 10]; + }); + + run_to_block(16); + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + //(6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + // 10 is assigned in place of 6 + (10, 1001), + ]), + ); + }); +} + +#[test] +fn assign_collators_after_one_leaves_orchestrator_chain() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + m.container_chains = vec![1001, 1002] + }); + + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + + MockData::mutate(|m| { + // Remove 4 + m.collators = vec![1, 2, 3, /*4,*/ 5, 6, 7, 8, 9, 10]; + }); + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + //(4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + // 10 is assigned in place of 4 + (10, 1000), + ]), + ); + }); +} + +#[test] +fn assign_collators_if_config_orchestrator_chain_collators_increases() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + m.container_chains = vec![1001, 1002] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + + MockData::mutate(|m| { + // Add 3 new collators to orchestrator_chain + m.min_orchestrator_chain_collators = 8; + m.max_orchestrator_chain_collators = 8; + }); + + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + (10, 1000), + (11, 1000), + (12, 1000), + ]), + ); + }); +} + +#[test] +fn assign_collators_if_config_orchestrator_chain_collators_decreases() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + m.container_chains = vec![1001, 1002] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + + MockData::mutate(|m| { + // Remove 3 collators from orchestrator_chain + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 2; + }); + + run_to_block(21); + + // The removed collators are random so no easy way to test the full list + assert_eq!(assigned_collators().len(), 6,); + }); +} + +#[test] +fn assign_collators_if_config_collators_per_container_increases() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + m.container_chains = vec![1001, 1002] + }); + + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + + MockData::mutate(|m| { + // Add 2 new collators to each container_chain + m.collators_per_container = 4; + }); + + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + (10, 1001), + (11, 1001), + (12, 1002), + (13, 1002), + ]), + ); + }); +} + +#[test] +fn assign_collators_if_container_chain_is_removed() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + m.container_chains = vec![1001, 1002] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + + MockData::mutate(|m| { + // Remove 1 container_chain + m.container_chains = vec![1001 /*1002*/]; + }); + + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + ]), + ); + }); +} + +#[test] +fn assign_collators_if_container_chain_is_added() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + m.container_chains = vec![1001, 1002] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + + MockData::mutate(|m| { + // Add 1 new container_chain + m.container_chains = vec![1001, 1002, 1003]; + }); + + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + (10, 1003), + (11, 1003), + ]), + ); + }); +} + +#[test] +fn assign_collators_after_decrease_num_collators() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + m.container_chains = vec![1001, 1002] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]); + assert_eq!(assigned_collators(), initial_assignment,); + + MockData::mutate(|m| { + m.collators = vec![]; + }); + + // Disable logs in this test because it will print: + // Error in collator assignment, will keep previous assignment. ZeroCollators + // But only if this test runs after: + // test mock::__construct_runtime_integrity_test::runtime_integrity_tests ... ok + // Because that test enables logging + silence_logs(|| { + run_to_block(21); + }); + + // There are no collators but that would brick the chain, so we keep the old assignment + assert_eq!(assigned_collators(), initial_assignment); + }); +} + +#[test] +fn assign_collators_stay_constant_if_new_collators_can_take_new_chains() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + m.container_chains = vec![]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![(1, 1000), (2, 1000), (3, 1000), (4, 1000), (5, 1000),]), + ); + + MockData::mutate(|m| { + m.container_chains = vec![1001, 1002]; + }); + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]), + ); + }); +} + +#[test] +fn assign_collators_move_extra_container_chain_to_orchestrator_chain_if_not_enough_collators() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4]; + m.container_chains = vec![]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![(1, 1000), (2, 1000), (3, 1000), (4, 1000),]), + ); + + MockData::mutate(|m| { + m.collators = vec![1, 2, 3, 4, 5]; + m.container_chains = vec![1001, 1002]; + }); + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![(1, 1000), (2, 1000), (3, 1000), (4, 1001), (5, 1001),]), + ); + }); +} + +#[test] +fn assign_collators_reorganize_container_chains_if_not_enough_collators() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + m.container_chains = vec![1001, 1002, 1003, 1004, 1005]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1001), + (4, 1001), + (5, 1002), + (6, 1002), + (7, 1003), + (8, 1003), + (9, 1004), + (10, 1004), + (11, 1005), + (12, 1005) + ]), + ); + + MockData::mutate(|m| { + // Remove collators to leave only 1 per container chain + m.collators = vec![1, 2, 3, 5, 7, 9, 11]; + }); + run_to_block(21); + + // There are 7 collators in total: 2x2 container chains, plus 3 in the orchestrator chain + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1001), + (5, 1002), + (7, 1000), + (9, 1001), + (11, 1002) + ]), + ); + }); +} + +#[test] +fn assign_collators_set_zero_per_container() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + m.container_chains = vec![1001, 1002, 1003, 1004]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1001), + (6, 1001), + (7, 1002), + (8, 1002), + (9, 1003), + (10, 1003), + (11, 1004), + (12, 1004), + ]), + ); + + MockData::mutate(|m| { + // We don't want to assign collators to container chains anymore + m.collators_per_container = 0; + }); + run_to_block(21); + + // There are 5 collators in total: 0x4 container chains, plus 5 in the orchestrator chain + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![(1, 1000), (2, 1000), (3, 1000), (4, 1000), (5, 1000),]), + ); + }); +} + +#[test] +fn assign_collators_rotation() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + m.container_chains = vec![1001, 1002, 1003, 1004]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1001), + (6, 1001), + (7, 1002), + (8, 1002), + (9, 1003), + (10, 1003), + (11, 1004), + (12, 1004), + ]); + + assert_eq!(assigned_collators(), initial_assignment,); + + MockData::mutate(|m| { + m.random_seed = [1; 32]; + }); + + // The rotation period is every 5 sessions, so the first session with a different assignment + // will be session 5. Collators are calculated one session in advance, so they will be decided + // on session 4. + run_to_block(20); + + assert_eq!(assigned_collators(), initial_assignment,); + assert_eq!(PendingCollatorContainerChain::::get(), None,); + + run_to_block(21); + assert_eq!(assigned_collators(), initial_assignment,); + + assert!(PendingCollatorContainerChain::::get().is_some(),); + + run_to_block(25); + assert_eq!(assigned_collators(), initial_assignment,); + run_to_block(26); + + // Random assignment depends on the seed, shouldn't change unless the algorithm changes + let shuffled_assignment = BTreeMap::from_iter(vec![ + (1, 1003), + (2, 1000), + (3, 1001), + (4, 1003), + (5, 1000), + (6, 1000), + (7, 1001), + (8, 1000), + (9, 1004), + (10, 1004), + (11, 1002), + (12, 1002), + ]); + + assert_eq!(assigned_collators(), shuffled_assignment); + }); +} + +#[test] +fn assign_collators_rotation_container_chains_are_shuffled() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + // 4 collators so we can only assign to one container chain + m.collators = vec![1, 2, 3, 4]; + m.container_chains = vec![1001, 1002]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = + BTreeMap::from_iter(vec![(1, 1000), (2, 1000), (3, 1001), (4, 1001)]); + + assert_eq!(assigned_collators(), initial_assignment,); + + MockData::mutate(|m| { + // Seed chosen manually to see the case where container 1002 is given priority + m.random_seed = [1; 32]; + }); + + run_to_block(26); + + // Random assignment depends on the seed, shouldn't change unless the algorithm changes + // Test that container chains are shuffled because 1001 does not have priority + let shuffled_assignment = + BTreeMap::from_iter(vec![(1, 1002), (2, 1000), (3, 1000), (4, 1002)]); + + assert_eq!(assigned_collators(), shuffled_assignment,); + }); +} + +#[test] +fn assign_collators_rotation_parathreads_are_shuffled() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + // 4 collators so we can only assign to one parathread + m.collators = vec![1, 2, 3, 4]; + m.parathreads = vec![3001, 3002]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = + BTreeMap::from_iter(vec![(1, 1000), (2, 1000), (3, 3001), (4, 3001)]); + + assert_eq!(assigned_collators(), initial_assignment,); + + MockData::mutate(|m| { + // Seed chosen manually to see the case where parathread 3002 is given priority + m.random_seed = [1; 32]; + }); + + run_to_block(26); + + // Random assignment depends on the seed, shouldn't change unless the algorithm changes + // Test that container chains are shuffled because 1001 does not have priority + let shuffled_assignment = + BTreeMap::from_iter(vec![(1, 3002), (2, 1000), (3, 1000), (4, 3002)]); + + assert_eq!(assigned_collators(), shuffled_assignment,); + }); +} + +#[test] +fn assign_collators_rotation_collators_are_shuffled() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + // 10 collators but we only need 9, so 1 collator will not be assigned + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + m.container_chains = vec![1001, 1002]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]); + + assert_eq!(assigned_collators(), initial_assignment,); + + MockData::mutate(|m| { + m.random_seed = [1; 32]; + }); + + run_to_block(26); + + // Random assignment depends on the seed, shouldn't change unless the algorithm changes + // Test that collators are shuffled and the collators of each container chain are not + // consecutive in order. For example, if collators 8 and 9 are both assigned to chain 1002, + // change the random seed until they are on different chains. + // Collator 10 will never be assigned because of the collator priority. + let shuffled_assignment = BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1001), + (3, 1000), + (4, 1000), + (5, 1002), + (6, 1001), + (7, 1000), + (8, 1002), + (9, 1000), + ]); + + assert_eq!(assigned_collators(), shuffled_assignment,); + }); +} + +#[test] +fn assign_collators_invulnerables_priority_orchestrator() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + // 11 collators but we only need 9, so 2 collator will not be assigned + // id 100 is an invulnerable so it will be assigned to the orchestrator + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100]; + m.container_chains = vec![1001, 1002]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = BTreeMap::from_iter(vec![ + (100, 1000), + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1001), + (6, 1001), + (7, 1002), + (8, 1002), + ]); + + assert_eq!(assigned_collators(), initial_assignment,); + }); +} + +#[test] +fn assign_collators_invulnerables_priority_orchestrator_reassigned() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + // Disable rotation because this test is long + m.full_rotation_period = Some(0); + + // 10 collators but we only need 9, so 1 collator will not be assigned + // ids >= 100 are invulnerables so 2 of them will always be assigned to the orchestrator + m.collators = vec![1, 2, 3, 4, 5, 100, 101, 102, 103, 104]; + m.container_chains = vec![1001, 1002]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = BTreeMap::from_iter(vec![ + (100, 1000), + (101, 1000), + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1001), + (5, 1001), + (102, 1002), + (103, 1002), + ]); + + assert_eq!(assigned_collators(), initial_assignment,); + + MockData::mutate(|m| { + // Remove invulnerable from orchestrator, the unassigned invulnerable will take its place + m.collators = vec![1, 2, 3, 4, 5, 101, 102, 103, 104]; + }); + + run_to_block(21); + + let assignment = BTreeMap::from_iter(vec![ + (104, 1000), + (101, 1000), + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1001), + (5, 1001), + (102, 1002), + (103, 1002), + ]); + + assert_eq!(assigned_collators(), assignment,); + + MockData::mutate(|m| { + // Remove another invulnerable from orchestrator, there are no unassigned invulnerables so the ones in a + // container chain will move from the container chain to the orchestrator + m.collators = vec![1, 2, 3, 4, 5, 102, 103, 104]; + }); + + run_to_block(31); + + let assignment = BTreeMap::from_iter(vec![ + (104, 1000), + (102, 1000), + (1, 1000), + (2, 1000), + (3, 1002), + (4, 1001), + (5, 1001), + (103, 1002), + ]); + + assert_eq!(assigned_collators(), assignment,); + }); +} + +#[test] +fn assign_collators_all_invulnerables() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + // All collators are invulnerables: this results in the same assignment as if there were not invulnerables + m.collators = vec![101, 102, 103, 104, 105, 106, 107, 108, 109, 110]; + m.container_chains = vec![1001, 1002]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = BTreeMap::from_iter(vec![ + (101, 1000), + (102, 1000), + (103, 1000), + (104, 1000), + (105, 1000), + (106, 1001), + (107, 1001), + (108, 1002), + (109, 1002), + ]); + + assert_eq!(assigned_collators(), initial_assignment,); + }); +} + +#[test] +fn rotation_events() { + // Ensure that the NewPendingAssignment event is correct + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + m.container_chains = vec![1001, 1002, 1003, 1004]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + + // Block 1 should emit event, random seed was not set + System::assert_last_event( + Event::NewPendingAssignment { + random_seed: [0; 32], + full_rotation: false, + target_session: 1, + } + .into(), + ); + + for i in 2..=11 { + run_to_block(i); + match i { + 6 | 11 => { + System::assert_last_event( + Event::NewPendingAssignment { + random_seed: [0; 32], + full_rotation: false, + target_session: (i / 5) as u32 + 1, + } + .into(), + ); + } + _ => { + assert_eq!( + System::events(), + vec![], + "Block #{} should not have any events", + i + ); + } + } + } + + MockData::mutate(|m| { + m.random_seed = [1; 32]; + }); + + // The rotation period is every 5 sessions, so the first session with a different assignment + // will be session 5. Collators are calculated one session in advance, so they will be decided + // on session 4, which starts on block 21. + for i in 12..=51 { + run_to_block(i); + match i { + 16 | 26 | 31 | 36 | 41 | 51 => { + System::assert_last_event( + Event::NewPendingAssignment { + random_seed: [1; 32], + full_rotation: false, + target_session: (i / 5) as u32 + 1, + } + .into(), + ); + } + 21 | 46 => { + System::assert_last_event( + Event::NewPendingAssignment { + random_seed: [1; 32], + full_rotation: true, + target_session: (i / 5) as u32 + 1, + } + .into(), + ); + } + _ => { + assert_eq!( + System::events(), + vec![], + "Block #{} should not have any events", + i + ); + } + } + } + }); +} + +#[test] +fn assign_collators_remove_from_orchestator_when_all_assigned() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 2; + + m.collators = vec![1, 2]; + m.container_chains = vec![1001]; + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let initial_assignment = BTreeMap::from_iter(vec![(1, 1000), (2, 1000)]); + + assert_eq!(assigned_collators(), initial_assignment,); + + MockData::mutate(|m| { + m.collators = vec![1, 2, 3, 4]; + }); + + run_to_block(26); + + let assignment = BTreeMap::from_iter(vec![(1, 1000), (2, 1000), (3, 1001), (4, 1001)]); + assert_eq!(assigned_collators(), assignment,); + + MockData::mutate(|m| { + m.collators = vec![1, 3, 4]; + }); + + run_to_block(36); + + let assignment = BTreeMap::from_iter(vec![(1, 1000), (3, 1000)]); + + assert_eq!(assigned_collators(), assignment,); + + MockData::mutate(|m| { + m.collators = vec![3, 4]; + }); + + run_to_block(46); + + let assignment = BTreeMap::from_iter(vec![(3, 1000), (4, 1000)]); + + assert_eq!(assigned_collators(), assignment,); + }); +} + +#[test] +fn collator_assignment_includes_empty_chains() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 2; + + m.collators = vec![1, 2]; + m.container_chains = vec![2000, 2001, 2002]; + m.parathreads = vec![3000, 3001, 3002] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let assigned_collators = CollatorContainerChain::::get(); + let expected = AssignedCollators { + orchestrator_chain: vec![1, 2], + container_chains: BTreeMap::from_iter(vec![ + (2000.into(), vec![]), + (2001.into(), vec![]), + (2002.into(), vec![]), + (3000.into(), vec![]), + (3001.into(), vec![]), + (3002.into(), vec![]), + ]), + }; + assert_eq!(assigned_collators, expected); + }); +} + +#[test] +fn collator_assignment_remove_parachains_without_credits() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7]; + m.container_chains = vec![2000, 5001, 5002]; + m.parathreads = vec![] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let assigned_collators = CollatorContainerChain::::get(); + let expected = AssignedCollators { + orchestrator_chain: vec![1, 2, 3, 4, 5], + container_chains: BTreeMap::from_iter(vec![(2000.into(), vec![6, 7])]), + }; + assert_eq!(assigned_collators, expected); + }); +} + +#[test] +fn collator_assignment_remove_parathreads_without_credits() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 2; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7]; + m.container_chains = vec![]; + m.parathreads = vec![3000, 5001, 5002] + }); + assert_eq!(assigned_collators(), initial_collators(),); + run_to_block(11); + + let assigned_collators = CollatorContainerChain::::get(); + let expected = AssignedCollators { + orchestrator_chain: vec![1, 2, 3, 4, 5], + container_chains: BTreeMap::from_iter(vec![(3000.into(), vec![6, 7])]), + }; + assert_eq!(assigned_collators, expected); + }); +} + +#[test] +fn assign_collators_prioritizing_tip() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; + m.container_chains = vec![1001, 1002, 1003, 1004]; + m.apply_tip = false + }); + + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + ]) + ); + + // Enable tip for 1003 and 1004 + MockData::mutate(|m| m.apply_tip = true); + + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1003), + (7, 1003), + (8, 1004), + (9, 1004), + ]), + ); + }); +} + +#[test] +fn on_collators_assigned_hook_failure_removes_para_from_assignment() { + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + m.collators_per_container = 2; + m.collators_per_parathread = 2; + m.min_orchestrator_chain_collators = 5; + m.max_orchestrator_chain_collators = 5; + + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + m.container_chains = vec![1001, 1002, 1003, 1004]; + m.assignment_hook_errors = false; + }); + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1001), + (7, 1001), + (8, 1002), + (9, 1002), + (10, 1003), + (11, 1003), + ]), + ); + + // Para 1001 will fail on_assignment_hook + MockData::mutate(|m| { + m.assignment_hook_errors = true; + }); + + run_to_block(21); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (8, 1002), + (9, 1002), + (10, 1003), + (11, 1003), + ]), + ); + }); +} + +#[test] +fn assign_collators_truncates_before_shuffling() { + // Check that if there are more collators than needed, we only assign the first collators + new_test_ext().execute_with(|| { + run_to_block(1); + + MockData::mutate(|m| { + // Need 5 collators in total, 3 for orchestrator and 2 for 1 container chain + m.collators_per_container = 2; + m.min_orchestrator_chain_collators = 3; + m.max_orchestrator_chain_collators = 3; + + // Have 10 collators in total, but only the first 5 will be assigned, in random order + m.collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + m.container_chains = vec![1001]; + m.random_seed = [1; 32]; + }); + + run_to_block(11); + + assert_eq!( + assigned_collators(), + BTreeMap::from_iter(vec![(1, 1001), (2, 1000), (3, 1000), (4, 1001), (5, 1000),]) + ); + }); +} diff --git a/pallets/collator-assignment/src/tests/assign_full.rs b/pallets/collator-assignment/src/tests/assign_full.rs new file mode 100644 index 0000000..047cff9 --- /dev/null +++ b/pallets/collator-assignment/src/tests/assign_full.rs @@ -0,0 +1,142 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + assignment::{Assignment, AssignmentError}, + tests::Test, + }, + rand::{seq::SliceRandom, SeedableRng}, + rand_chacha::ChaCha20Rng, + sp_std::collections::btree_map::BTreeMap, +}; + +fn no_shuffle() -> Option)> { + None +} + +#[test] +fn assign_full_old_assigned_priority() { + // Collators in old_assigned will be selected before other collators + let collators = vec![1, 2, 3, 4, 5]; + let container_chains = vec![(1000.into(), 5)]; + let old_assigned = BTreeMap::from_iter(vec![(1000.into(), vec![3, 4])]); + + let new_assigned = + Assignment::::assign_full(collators, container_chains, old_assigned, no_shuffle()) + .unwrap(); + let expected = BTreeMap::from_iter(vec![(1000.into(), vec![3, 4, 1, 2, 5])]); + assert_eq!(new_assigned, expected); +} + +#[test] +fn assign_full_invalid_old_assigned_collators_removed() { + // If the collators in old_assigned are no longer collators, they are not assigned + let collators = vec![1, 2, 3, 4, 5]; + let container_chains = vec![(1000.into(), 5)]; + let old_assigned = BTreeMap::from_iter(vec![(1000.into(), vec![20, 21])]); + + let new_assigned = + Assignment::::assign_full(collators, container_chains, old_assigned, no_shuffle()) + .unwrap(); + let expected = BTreeMap::from_iter(vec![(1000.into(), vec![1, 2, 3, 4, 5])]); + assert_eq!(new_assigned, expected); +} + +#[test] +fn assign_full_invalid_chains_removed() { + // Mark all collators as already assigned to a chain that does not exist. Should treat them as not assigned. + let collators = vec![1, 2, 3, 4, 5]; + let container_chains = vec![(1000.into(), 5)]; + let old_assigned = BTreeMap::from_iter(vec![(1001.into(), vec![1, 2, 3, 4, 5])]); + + let new_assigned = + Assignment::::assign_full(collators, container_chains, old_assigned, no_shuffle()) + .unwrap(); + let expected = BTreeMap::from_iter(vec![(1000.into(), vec![1, 2, 3, 4, 5])]); + assert_eq!(new_assigned, expected); +} + +#[test] +fn assign_full_truncates_collators() { + // Need 2 collators for each chain, when old_assigned has more than 2. Should truncate old_assigned to 2. + let collators = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let container_chains = vec![(1000.into(), 2), (2000.into(), 2)]; + let old_assigned = BTreeMap::from_iter(vec![ + (1000.into(), vec![1, 2, 3, 4, 5]), + (2000.into(), vec![6, 7, 8, 9, 10]), + ]); + + let new_assigned = + Assignment::::assign_full(collators, container_chains, old_assigned, no_shuffle()) + .unwrap(); + let expected = BTreeMap::from_iter(vec![(1000.into(), vec![1, 2]), (2000.into(), vec![6, 7])]); + assert_eq!(new_assigned, expected); +} + +#[test] +fn assign_full_old_assigned_error_if_not_enough_collators() { + // Need 4 collators, only have 2, and all 2 were assigned to the second chain. If the function did not panic, we + // would have 0 collators assigned to the first chain, which is supposed to have priority. + let collators = vec![1, 2]; + let container_chains = vec![(1000.into(), 2), (2000.into(), 2)]; + let old_assigned = BTreeMap::from_iter(vec![(2000.into(), vec![1, 2])]); + let new_assigned = + Assignment::::assign_full(collators, container_chains, old_assigned, no_shuffle()); + assert_eq!( + new_assigned.unwrap_err(), + AssignmentError::NotEnoughCollators + ); +} + +#[test] +fn assign_full_list_priority() { + // The order in the collators list is priority + let collators = vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + ]; + let container_chains = vec![(1000.into(), 2), (2000.into(), 2)]; + let old_assigned = BTreeMap::from_iter(vec![]); + + let new_assigned = + Assignment::::assign_full(collators, container_chains, old_assigned, no_shuffle()) + .unwrap(); + let expected = BTreeMap::from_iter(vec![(1000.into(), vec![1, 2]), (2000.into(), vec![3, 4])]); + assert_eq!(new_assigned, expected); +} + +#[test] +fn assign_full_list_priority_shuffle() { + // The order in the collators list is priority + let collators = vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + ]; + let container_chains = vec![(1000.into(), 2), (2000.into(), 2)]; + let old_assigned = BTreeMap::from_iter(vec![]); + let shuffle = Some(move |collators: &mut Vec| { + // Shuffle with a fixed seed, we do not need randomness in a unit test + let seed = [1; 32]; + let mut rng: ChaCha20Rng = SeedableRng::from_seed(seed); + collators.shuffle(&mut rng); + }); + + let new_assigned = + Assignment::::assign_full(collators, container_chains, old_assigned, shuffle) + .unwrap(); + // Expect only [1, 2, 3, 4] to be assigned, in random order + let expected = BTreeMap::from_iter(vec![(1000.into(), vec![3, 2]), (2000.into(), vec![1, 4])]); + assert_eq!(new_assigned, expected); +} diff --git a/pallets/collator-assignment/src/tests/prioritize_invulnerables.rs b/pallets/collator-assignment/src/tests/prioritize_invulnerables.rs new file mode 100644 index 0000000..4e13c69 --- /dev/null +++ b/pallets/collator-assignment/src/tests/prioritize_invulnerables.rs @@ -0,0 +1,186 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + assignment::{Assignment, ChainNumCollators}, + tests::Test, + }, + sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}, +}; + +#[test] +fn invulnerable_priority_0_collators() { + let collators = vec![]; + let orchestrator_chain = ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }; + let mut old_assigned = BTreeMap::new(); + + let num_invulnerables = Assignment::::prioritize_invulnerables( + &collators, + orchestrator_chain, + &mut old_assigned, + ); + + assert_eq!(num_invulnerables, 0); +} + +#[test] +fn invulnerable_priority_0_invulnerables() { + let collators = vec![1, 2, 3, 4, 5]; + let orchestrator_chain = ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }; + let mut old_assigned = BTreeMap::from_iter(vec![(1000.into(), vec![1, 2])]); + + let num_invulnerables = Assignment::::prioritize_invulnerables( + &collators, + orchestrator_chain, + &mut old_assigned, + ); + + assert_eq!(num_invulnerables, 0); +} + +#[test] +fn invulnerable_priority_1_invulnerable_orchestrator() { + let collators = vec![1, 2, 3, 4, 5, 101]; + let orchestrator_chain = ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }; + let mut old_assigned = BTreeMap::from_iter(vec![(1000.into(), vec![101])]); + + let num_invulnerables = Assignment::::prioritize_invulnerables( + &collators, + orchestrator_chain, + &mut old_assigned, + ); + + assert_eq!(num_invulnerables, 1); +} + +#[test] +fn invulnerable_priority_1_invulnerable_not_assigned() { + let collators = vec![1, 2, 3, 4, 5, 101]; + let orchestrator_chain = ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }; + let mut old_assigned = BTreeMap::new(); + + let num_invulnerables = Assignment::::prioritize_invulnerables( + &collators, + orchestrator_chain, + &mut old_assigned, + ); + + assert_eq!(num_invulnerables, 1); +} + +#[test] +fn invulnerable_priority_1_invulnerable_assigned_to_another_chain() { + let collators = vec![1, 2, 3, 4, 5, 101]; + let orchestrator_chain = ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }; + let mut old_assigned = + BTreeMap::from_iter(vec![(1000.into(), vec![]), (2000.into(), vec![101])]); + + let num_invulnerables = Assignment::::prioritize_invulnerables( + &collators, + orchestrator_chain, + &mut old_assigned, + ); + + assert_eq!(num_invulnerables, 1); +} + +#[test] +fn bug_same_invulnerable_selected_twice() { + let collators = vec![100]; + let orchestrator_chain = ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }; + let mut old_assigned = BTreeMap::from_iter(vec![(1000.into(), vec![100])]); + + let num_invulnerables = Assignment::::prioritize_invulnerables( + &collators, + orchestrator_chain, + &mut old_assigned, + ); + + assert_eq!(num_invulnerables, 1); +} + +#[test] +fn bug_not_using_assigned_invulnerables() { + // There are 3 invulnerables, 1 assigned to orchestrator and 2 assigned to a container chain. + // After `prioritize_invulnerables` the first one from the container should move to orchestrator + let collators = vec![1, 2, 3, 4, 5, 102, 103, 104]; + + let container_chains = [ + ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2000.into(), + min_collators: 2, + max_collators: 2, + }, + ChainNumCollators { + para_id: 2001.into(), + min_collators: 2, + max_collators: 2, + }, + ]; + let orchestrator_chain = container_chains[0]; + + let mut old_assigned = BTreeMap::from_iter(vec![ + (1000.into(), vec![101, 104, 1, 2, 3]), + (2000.into(), vec![4, 5]), + (2001.into(), vec![102, 103]), + ]); + + let chains_with_collators_set = + BTreeSet::from_iter(container_chains.iter().map(|cc| cc.para_id)); + let collators_set = BTreeSet::from_iter(collators.iter().cloned()); + Assignment::::retain_valid_old_assigned( + &mut old_assigned, + &chains_with_collators_set, + &collators_set, + ); + let num_invulnerables = Assignment::::prioritize_invulnerables( + &collators, + orchestrator_chain, + &mut old_assigned, + ); + + assert_eq!(num_invulnerables, 2); +} diff --git a/pallets/collator-assignment/src/tests/select_chains.rs b/pallets/collator-assignment/src/tests/select_chains.rs new file mode 100644 index 0000000..c524c0c --- /dev/null +++ b/pallets/collator-assignment/src/tests/select_chains.rs @@ -0,0 +1,176 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use crate::{ + assignment::{Assignment, ChainNumCollators}, + tests::Test, +}; + +#[test] +fn select_chains_not_enough_to_reach_min_container() { + // 10 collators when the orchestrator needs 2 and the containers need 10 result in no containers having collators + let container_chains = vec![ + ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2000.into(), + min_collators: 10, + max_collators: 10, + }, + ChainNumCollators { + para_id: 2001.into(), + min_collators: 10, + max_collators: 10, + }, + ]; + let new_assigned = Assignment::::select_chains_with_collators(10, &container_chains); + assert_eq!(new_assigned, vec![(1000.into(), 5),]); +} + +#[test] +fn select_chains_not_enough_to_reach_min_orchestrator() { + // 1 collator when the orchestrator needs 2 results in 1 collators being assigned to orchestrator + let container_chains = vec![ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }]; + let new_assigned = Assignment::::select_chains_with_collators(1, &container_chains); + assert_eq!(new_assigned, vec![(1000.into(), 1),]); +} + +#[test] +fn select_chains_not_enough_for_all_min() { + // Need 6 collators to support 3 chains, only have 5. The last chain will be removed and the remaining collator + // will be assigned to orchestrator. + let container_chains = vec![ + ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2000.into(), + min_collators: 2, + max_collators: 2, + }, + ChainNumCollators { + para_id: 2001.into(), + min_collators: 2, + max_collators: 2, + }, + ]; + let new_assigned = Assignment::::select_chains_with_collators(5, &container_chains); + assert_eq!(new_assigned, vec![(1000.into(), 3), (2000.into(), 2),]); +} + +#[test] +fn select_chains_not_enough_for_all_max() { + // Need 6 collators to support 3 chains at min, but 15 collators to support them at max. + // The last chain will be removed and the remaining collator + let container_chains = vec![ + ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2001.into(), + min_collators: 2, + max_collators: 5, + }, + ]; + let new_assigned = Assignment::::select_chains_with_collators(7, &container_chains); + assert_eq!( + new_assigned, + vec![(1000.into(), 3), (2000.into(), 2), (2001.into(), 2),] + ); + let new_assigned = Assignment::::select_chains_with_collators(10, &container_chains); + assert_eq!( + new_assigned, + vec![(1000.into(), 5), (2000.into(), 3), (2001.into(), 2),] + ); + let new_assigned = Assignment::::select_chains_with_collators(13, &container_chains); + assert_eq!( + new_assigned, + vec![(1000.into(), 5), (2000.into(), 5), (2001.into(), 3),] + ); + let new_assigned = Assignment::::select_chains_with_collators(15, &container_chains); + assert_eq!( + new_assigned, + vec![(1000.into(), 5), (2000.into(), 5), (2001.into(), 5),] + ); +} + +#[test] +fn select_chains_more_than_max() { + // When the number of collators is greater than the sum of the max, all the chains are assigned max collators + let container_chains = vec![ + ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2001.into(), + min_collators: 2, + max_collators: 5, + }, + ]; + let new_assigned = Assignment::::select_chains_with_collators(20, &container_chains); + assert_eq!( + new_assigned, + vec![(1000.into(), 5), (2000.into(), 5), (2001.into(), 5),] + ); +} + +#[test] +fn select_chains_not_enough_to_reach_min_container_but_enough_for_parathread() { + // Chain 2000 has more priority than parathread 3000, but we do not have enough min collators so the container + // chain gets 0 collator and the parathread gets 1 + let container_chains = vec![ + ChainNumCollators { + para_id: 1000.into(), + min_collators: 2, + max_collators: 5, + }, + ChainNumCollators { + para_id: 2000.into(), + min_collators: 2, + max_collators: 2, + }, + ChainNumCollators { + para_id: 3000.into(), + min_collators: 1, + max_collators: 1, + }, + ]; + let new_assigned = Assignment::::select_chains_with_collators(3, &container_chains); + assert_eq!(new_assigned, vec![(1000.into(), 2), (3000.into(), 1)]); +} diff --git a/pallets/collator-assignment/src/weights.rs b/pallets/collator-assignment/src/weights.rs new file mode 100644 index 0000000..147d121 --- /dev/null +++ b/pallets/collator-assignment/src/weights.rs @@ -0,0 +1,139 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_collator_assignment +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `tomasz-XPS-15-9520`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_collator_assignment +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_collator_assignment. +pub trait WeightInfo { + fn new_session(x: u32, y: u32, ) -> Weight; +} + +/// Weights for pallet_collator_assignment using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `CollatorAssignment::Randomness` (r:1 w:1) + /// Proof: `CollatorAssignment::Randomness` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParaIds` (r:1 w:0) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegisteredParaIds` (r:1 w:0) + /// Proof: `Registrar::RegisteredParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::BlockProductionCredits` (r:20 w:0) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `CollatorAssignment::PendingCollatorContainerChain` (r:1 w:1) + /// Proof: `CollatorAssignment::PendingCollatorContainerChain` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::ActiveConfig` (r:1 w:0) + /// Proof: `Configuration::ActiveConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::PendingConfigs` (r:1 w:0) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Invulnerables::Invulnerables` (r:1 w:0) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + /// Storage: `CollatorAssignment::CollatorContainerChain` (r:0 w:1) + /// Proof: `CollatorAssignment::CollatorContainerChain` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + /// The range of component `y` is `[1, 20]`. + fn new_session(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `766 + y * (32 ±0)` + // Estimated: `4687 + y * (2499 ±0)` + // Minimum execution time: 56_626_000 picoseconds. + Weight::from_parts(41_872_969, 4687) + // Standard Error: 1_156 + .saturating_add(Weight::from_parts(76_061, 0).saturating_mul(x.into())) + // Standard Error: 11_776 + .saturating_add(Weight::from_parts(2_758_758, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 2499).saturating_mul(y.into())) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `CollatorAssignment::Randomness` (r:1 w:1) + /// Proof: `CollatorAssignment::Randomness` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParaIds` (r:1 w:0) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegisteredParaIds` (r:1 w:0) + /// Proof: `Registrar::RegisteredParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::BlockProductionCredits` (r:20 w:0) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `CollatorAssignment::PendingCollatorContainerChain` (r:1 w:1) + /// Proof: `CollatorAssignment::PendingCollatorContainerChain` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::ActiveConfig` (r:1 w:0) + /// Proof: `Configuration::ActiveConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::PendingConfigs` (r:1 w:0) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Invulnerables::Invulnerables` (r:1 w:0) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + /// Storage: `CollatorAssignment::CollatorContainerChain` (r:0 w:1) + /// Proof: `CollatorAssignment::CollatorContainerChain` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + /// The range of component `y` is `[1, 20]`. + fn new_session(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `766 + y * (32 ±0)` + // Estimated: `4687 + y * (2499 ±0)` + // Minimum execution time: 56_626_000 picoseconds. + Weight::from_parts(41_872_969, 4687) + // Standard Error: 1_156 + .saturating_add(Weight::from_parts(76_061, 0).saturating_mul(x.into())) + // Standard Error: 11_776 + .saturating_add(Weight::from_parts(2_758_758, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 2499).saturating_mul(y.into())) + } +} diff --git a/pallets/configuration/Cargo.toml b/pallets/configuration/Cargo.toml new file mode 100644 index 0000000..ec8d9db --- /dev/null +++ b/pallets/configuration/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "pallet-configuration" +authors = { workspace = true } +description = "Configuration pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive", "max-encoded-len" ] } +scale-info = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +tp-traits = { workspace = true } + +[dev-dependencies] +sp-io = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "parity-scale-codec/std", + "scale-info/std", + "serde/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/configuration/src/benchmarks.rs b/pallets/configuration/src/benchmarks.rs new file mode 100644 index 0000000..6dcb033 --- /dev/null +++ b/pallets/configuration/src/benchmarks.rs @@ -0,0 +1,34 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#![cfg(feature = "runtime-benchmarks")] + +//! Benchmarking +use { + crate::{Call, Config, Pallet}, + frame_benchmarking::benchmarks, + frame_system::RawOrigin, +}; + +benchmarks! { + set_config_with_u32 {}: set_max_collators(RawOrigin::Root, 100) + + impl_benchmark_test_suite!( + Pallet, + crate::mock::new_test_ext(), + crate::mock::Test + ); +} diff --git a/pallets/configuration/src/lib.rs b/pallets/configuration/src/lib.rs new file mode 100644 index 0000000..40f037d --- /dev/null +++ b/pallets/configuration/src/lib.rs @@ -0,0 +1,597 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Configuration Pallet +//! +//! This pallet stores the configuration for an orchestration-collator assignation chain. In +//! particular stores: +//! +//! - How many collators are taken. +//! - How many of those collators should be serving the orchestrator chain +//! - Howe many of those collators should be serving the containerChains +//! +//! All configuration changes are protected behind the root origin +//! CHanges to the configuration are not immeditaly applied, but rather we wait +//! T::SessionDelay to apply these changes + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; +pub mod weights; + +pub use weights::WeightInfo; + +#[cfg(any(test, feature = "runtime-benchmarks"))] +mod benchmarks; + +pub use pallet::*; +use { + frame_support::pallet_prelude::*, + frame_system::pallet_prelude::*, + serde::{Deserialize, Serialize}, + sp_runtime::{traits::AtLeast32BitUnsigned, Perbill, RuntimeAppPublic, Saturating}, + sp_std::prelude::*, + tp_traits::GetSessionIndex, +}; + +const LOG_TARGET: &str = "pallet_configuration"; + +/// All configuration of the runtime with respect to parachains and parathreads. +#[derive( + Clone, + Encode, + Decode, + PartialEq, + sp_core::RuntimeDebug, + scale_info::TypeInfo, + Serialize, + Deserialize, +)] +pub struct HostConfiguration { + /// Maximum number of collators, in total, including orchestrator and containers + pub max_collators: u32, + /// Minimum number of collators to be assigned to orchestrator chain + pub min_orchestrator_collators: u32, + /// Maximum number of collators to be assigned to orchestrator chain after all the container chains have been + /// assigned collators. + pub max_orchestrator_collators: u32, + /// How many collators to assign to one container chain + pub collators_per_container: u32, + /// Rotate all collators once every n sessions. If this value is 0 means that there is no rotation + pub full_rotation_period: u32, + /// How many collators to assign to one parathread + // TODO: for now we only support 1 collator per parathread because using Aura for consensus conflicts with + // the idea of being able to create blocks every n slots: if there are 2 collators and we create blocks + // every 2 slots, 1 collator will create all the blocks. + pub collators_per_parathread: u32, + /// How many parathreads can be assigned to one collator + pub parathreads_per_collator: u32, + /// Ratio of collators that we expect to be assigned to container chains. Affects fees. + pub target_container_chain_fullness: Perbill, +} + +impl Default for HostConfiguration { + fn default() -> Self { + Self { + max_collators: 100u32, + min_orchestrator_collators: 2u32, + max_orchestrator_collators: 5u32, + collators_per_container: 2u32, + full_rotation_period: 24u32, + collators_per_parathread: 1, + parathreads_per_collator: 1, + target_container_chain_fullness: Perbill::from_percent(80), + } + } +} + +/// Enumerates the possible inconsistencies of `HostConfiguration`. +#[derive(Debug)] +pub enum InconsistentError { + /// `max_orchestrator_collators` is lower than `min_orchestrator_collators` + MaxCollatorsLowerThanMinCollators, + /// `min_orchestrator_collators` must be at least 1 + MinOrchestratorCollatorsTooLow, + /// `max_collators` must be at least 1 + MaxCollatorsTooLow, + /// Tried to modify an unimplemented parameter + UnimplementedParameter, +} + +impl HostConfiguration { + /// Checks that this instance is consistent with the requirements on each individual member. + /// + /// # Errors + /// + /// This function returns an error if the configuration is inconsistent. + pub fn check_consistency(&self) -> Result<(), InconsistentError> { + if self.max_collators < 1 { + return Err(InconsistentError::MaxCollatorsTooLow); + } + if self.min_orchestrator_collators < 1 { + return Err(InconsistentError::MinOrchestratorCollatorsTooLow); + } + if self.max_orchestrator_collators < self.min_orchestrator_collators { + return Err(InconsistentError::MaxCollatorsLowerThanMinCollators); + } + if self.parathreads_per_collator != 1 { + return Err(InconsistentError::UnimplementedParameter); + } + if self.max_collators < self.min_orchestrator_collators { + return Err(InconsistentError::MaxCollatorsLowerThanMinCollators); + } + Ok(()) + } + + /// Checks that this instance is consistent with the requirements on each individual member. + /// + /// # Panics + /// + /// This function panics if the configuration is inconsistent. + pub fn panic_if_not_consistent(&self) { + if let Err(err) = self.check_consistency() { + panic!("Host configuration is inconsistent: {:?}", err); + } + } +} + +#[frame_support::pallet] +pub mod pallet { + use tp_traits::GetHostConfiguration; + + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned; + + // `SESSION_DELAY` is used to delay any changes to Paras registration or configurations. + // Wait until the session index is 2 larger then the current index to apply any changes, + // which guarantees that at least one full session has passed before any changes are applied. + #[pallet::constant] + type SessionDelay: Get; + + type CurrentSessionIndex: GetSessionIndex; + + /// The identifier type for an authority. + type AuthorityId: Member + + Parameter + + RuntimeAppPublic + + MaybeSerializeDeserialize + + MaxEncodedLen; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + /// The new value for a configuration parameter is invalid. + InvalidNewValue, + } + + /// The active configuration for the current session. + #[pallet::storage] + #[pallet::getter(fn config)] + pub(crate) type ActiveConfig = StorageValue<_, HostConfiguration, ValueQuery>; + + /// Pending configuration changes. + /// + /// This is a list of configuration changes, each with a session index at which it should + /// be applied. + /// + /// The list is sorted ascending by session index. Also, this list can only contain at most + /// 2 items: for the next session and for the `scheduled_session`. + #[pallet::storage] + #[pallet::getter(fn pending_configs)] + pub(crate) type PendingConfigs = + StorageValue<_, Vec<(T::SessionIndex, HostConfiguration)>, ValueQuery>; + + /// If this is set, then the configuration setters will bypass the consistency checks. This + /// is meant to be used only as the last resort. + #[pallet::storage] + pub(crate) type BypassConsistencyCheck = StorageValue<_, bool, ValueQuery>; + + #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] + pub struct GenesisConfig { + pub config: HostConfiguration, + #[serde(skip)] + pub _config: sp_std::marker::PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + self.config.panic_if_not_consistent(); + ActiveConfig::::put(&self.config); + } + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_max_collators(origin: OriginFor, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.max_collators = new; + }) + } + + #[pallet::call_index(1)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_min_orchestrator_collators(origin: OriginFor, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + if config.max_orchestrator_collators < new { + config.max_orchestrator_collators = new; + } + config.min_orchestrator_collators = new; + }) + } + + #[pallet::call_index(2)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_max_orchestrator_collators(origin: OriginFor, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + if config.min_orchestrator_collators > new { + config.min_orchestrator_collators = new; + } + config.max_orchestrator_collators = new; + }) + } + + #[pallet::call_index(3)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_collators_per_container(origin: OriginFor, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.collators_per_container = new; + }) + } + + #[pallet::call_index(4)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_full_rotation_period(origin: OriginFor, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.full_rotation_period = new; + }) + } + + #[pallet::call_index(5)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_collators_per_parathread(origin: OriginFor, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.collators_per_parathread = new; + }) + } + + #[pallet::call_index(6)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_parathreads_per_collator(origin: OriginFor, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.parathreads_per_collator = new; + }) + } + + #[pallet::call_index(7)] + #[pallet::weight(( + T::WeightInfo::set_config_with_u32(), + DispatchClass::Operational, + ))] + pub fn set_target_container_chain_fullness( + origin: OriginFor, + new: Perbill, + ) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.target_container_chain_fullness = new; + }) + } + + /// Setting this to true will disable consistency checks for the configuration setters. + /// Use with caution. + #[pallet::call_index(44)] + #[pallet::weight(( + T::DbWeight::get().writes(1), + DispatchClass::Operational, + ))] + pub fn set_bypass_consistency_check(origin: OriginFor, new: bool) -> DispatchResult { + ensure_root(origin)?; + BypassConsistencyCheck::::put(new); + Ok(()) + } + } + + /// A struct that holds the configuration that was active before the session change and optionally + /// a configuration that became active after the session change. + pub struct SessionChangeOutcome { + /// Previously active configuration. + pub prev_config: HostConfiguration, + /// If new configuration was applied during the session change, this is the new configuration. + pub new_config: Option, + } + + impl Pallet { + /// Called by the initializer to note that a new session has started. + /// + /// Returns the configuration that was actual before the session change and the configuration + /// that became active after the session change. If there were no scheduled changes, both will + /// be the same. + pub fn initializer_on_new_session(session_index: &T::SessionIndex) -> SessionChangeOutcome { + let pending_configs = >::get(); + let prev_config = ActiveConfig::::get(); + + // No pending configuration changes, so we're done. + if pending_configs.is_empty() { + return SessionChangeOutcome { + prev_config, + new_config: None, + }; + } + + // We partition those configs scheduled for the present + // and those for the future + let (mut past_and_present, future) = pending_configs + .into_iter() + .partition::, _>(|&(apply_at_session, _)| { + apply_at_session <= *session_index + }); + + if past_and_present.len() > 1 { + // This should never happen since we schedule configuration changes only into the future + // sessions and this handler called for each session change. + log::error!( + target: LOG_TARGET, + "Skipping applying configuration changes scheduled sessions in the past", + ); + } + + let new_config = past_and_present.pop().map(|(_, config)| config); + if let Some(ref new_config) = new_config { + // Apply the new configuration. + ActiveConfig::::put(new_config); + } + + // We insert future as PendingConfig + >::put(future); + + SessionChangeOutcome { + prev_config, + new_config, + } + } + + /// Return the session index that should be used for any future scheduled changes. + fn scheduled_session() -> T::SessionIndex { + T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get()) + } + + /// Forcibly set the active config. This should be used with extreme care, and typically + /// only when enabling parachains runtime pallets for the first time on a chain which has + /// been running without them. + pub fn force_set_active_config(config: HostConfiguration) { + ActiveConfig::::set(config); + } + + /// This function should be used to update members of the configuration. + /// + /// This function is used to update the configuration in a way that is safe. It will check the + /// resulting configuration and ensure that the update is valid. If the update is invalid, it + /// will check if the previous configuration was valid. If it was invalid, we proceed with + /// updating the configuration, giving a chance to recover from such a condition. + /// + /// The actual configuration change take place after a couple of sessions have passed. In case + /// this function is called more than once in a session, then the pending configuration change + /// will be updated and the changes will be applied at once. + // NOTE: Explicitly tell rustc not to inline this because otherwise heuristics note the incoming + // closure making it's attractive to inline. However, in this case, we will end up with lots of + // duplicated code (making this function to show up in the top of heaviest functions) only for + // the sake of essentially avoiding an indirect call. Doesn't worth it. + #[inline(never)] + fn schedule_config_update(updater: impl FnOnce(&mut HostConfiguration)) -> DispatchResult { + let mut pending_configs = >::get(); + + // 1. pending_configs = [] + // No pending configuration changes. + // + // That means we should use the active config as the base configuration. We will insert + // the new pending configuration as (cur+2, new_config) into the list. + // + // 2. pending_configs = [(cur+2, X)] + // There is a configuration that is pending for the scheduled session. + // + // We will use X as the base configuration. We can update the pending configuration X + // directly. + // + // 3. pending_configs = [(cur+1, X)] + // There is a pending configuration scheduled and it will be applied in the next session. + // + // We will use X as the base configuration. We need to schedule a new configuration change + // for the `scheduled_session` and use X as the base for the new configuration. + // + // 4. pending_configs = [(cur+1, X), (cur+2, Y)] + // There is a pending configuration change in the next session and for the scheduled + // session. Due to case №3, we can be sure that Y is based on top of X. This means we + // can use Y as the base configuration and update Y directly. + // + // There cannot be (cur, X) because those are applied in the session change handler for the + // current session. + + // First, we need to decide what we should use as the base configuration. + let mut base_config = pending_configs + .last() + .map(|(_, config)| config.clone()) + .unwrap_or_else(Self::config); + let base_config_consistent = base_config.check_consistency().is_ok(); + + // Now, we need to decide what the new configuration should be. + // We also move the `base_config` to `new_config` to empahsize that the base config was + // destroyed by the `updater`. + updater(&mut base_config); + let new_config = base_config; + + if BypassConsistencyCheck::::get() { + // This will emit a warning each configuration update if the consistency check is + // bypassed. This is an attempt to make sure the bypass is not accidentally left on. + log::warn!( + target: LOG_TARGET, + "Bypassing the consistency check for the configuration change!", + ); + } else if let Err(e) = new_config.check_consistency() { + if base_config_consistent { + // Base configuration is consistent and the new configuration is inconsistent. + // This means that the value set by the `updater` is invalid and we can return + // it as an error. + log::warn!( + target: LOG_TARGET, + "Configuration change rejected due to invalid configuration: {:?}", + e, + ); + return Err(Error::::InvalidNewValue.into()); + } else { + // The configuration was already broken, so we can as well proceed with the update. + // You cannot break something that is already broken. + // + // That will allow to call several functions and ultimately return the configuration + // into consistent state. + log::warn!( + target: LOG_TARGET, + "The new configuration is broken but the old is broken as well. Proceeding", + ); + } + } + + let scheduled_session = Self::scheduled_session(); + + if let Some(&mut (_, ref mut config)) = pending_configs + .iter_mut() + .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session) + { + *config = new_config; + } else { + // We are scheduling a new configuration change for the scheduled session. + pending_configs.push((scheduled_session, new_config)); + } + + >::put(pending_configs); + + Ok(()) + } + } + + impl GetHostConfiguration for Pallet { + fn max_collators(session_index: T::SessionIndex) -> u32 { + let (past_and_present, _) = Pallet::::pending_configs() + .into_iter() + .partition::, _>(|&(apply_at_session, _)| apply_at_session <= session_index); + + let config = if let Some(last) = past_and_present.last() { + last.1.clone() + } else { + Pallet::::config() + }; + config.max_collators + } + + fn collators_per_container(session_index: T::SessionIndex) -> u32 { + let (past_and_present, _) = Pallet::::pending_configs() + .into_iter() + .partition::, _>(|&(apply_at_session, _)| apply_at_session <= session_index); + + let config = if let Some(last) = past_and_present.last() { + last.1.clone() + } else { + Pallet::::config() + }; + config.collators_per_container + } + + fn collators_per_parathread(session_index: T::SessionIndex) -> u32 { + let (past_and_present, _) = Pallet::::pending_configs() + .into_iter() + .partition::, _>(|&(apply_at_session, _)| apply_at_session <= session_index); + + let config = if let Some(last) = past_and_present.last() { + last.1.clone() + } else { + Pallet::::config() + }; + config.collators_per_parathread + } + + fn min_collators_for_orchestrator(session_index: T::SessionIndex) -> u32 { + let (past_and_present, _) = Pallet::::pending_configs() + .into_iter() + .partition::, _>(|&(apply_at_session, _)| apply_at_session <= session_index); + + let config = if let Some(last) = past_and_present.last() { + last.1.clone() + } else { + Pallet::::config() + }; + config.min_orchestrator_collators + } + + fn max_collators_for_orchestrator(session_index: T::SessionIndex) -> u32 { + let (past_and_present, _) = Pallet::::pending_configs() + .into_iter() + .partition::, _>(|&(apply_at_session, _)| apply_at_session <= session_index); + + let config = if let Some(last) = past_and_present.last() { + last.1.clone() + } else { + Pallet::::config() + }; + config.max_orchestrator_collators + } + } +} diff --git a/pallets/configuration/src/mock.rs b/pallets/configuration/src/mock.rs new file mode 100644 index 0000000..5ffa997 --- /dev/null +++ b/pallets/configuration/src/mock.rs @@ -0,0 +1,120 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{self as pallet_configuration, HostConfiguration}, + frame_support::traits::{ConstU16, ConstU64}, + frame_system as system, + sp_core::{ConstU32, H256}, + sp_runtime::{ + testing::UintAuthorityId, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, + }, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Configuration: pallet_configuration, + } +); + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = (); +} + +pub struct CurrentSessionIndexGetter; + +impl pallet_configuration::GetSessionIndex for CurrentSessionIndexGetter { + /// Returns current session index. + fn session_index() -> u32 { + // For tests, let 1 session be 5 blocks + (System::block_number() / 5) as u32 + } +} + +impl pallet_configuration::Config for Test { + type WeightInfo = (); + type SessionDelay = ConstU32<2>; + type SessionIndex = u32; + type CurrentSessionIndex = CurrentSessionIndexGetter; + type AuthorityId = UintAuthorityId; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext_with_genesis(config: HostConfiguration) -> sp_io::TestExternalities { + RuntimeGenesisConfig { + system: Default::default(), + configuration: pallet_configuration::GenesisConfig { + config, + ..Default::default() + }, + } + .build_storage() + .unwrap() + .into() +} + +pub fn run_to_block(n: u64) { + let old_block_number = System::block_number(); + let session_len = 5; + + for x in (old_block_number + 1)..=n { + System::reset_events(); + System::set_block_number(x); + + if x % session_len == 1 { + let session_index = (x / session_len) as u32; + Configuration::initializer_on_new_session(&session_index); + } + } +} diff --git a/pallets/configuration/src/tests.rs b/pallets/configuration/src/tests.rs new file mode 100644 index 0000000..0e36b10 --- /dev/null +++ b/pallets/configuration/src/tests.rs @@ -0,0 +1,446 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{mock::*, Error, HostConfiguration, PendingConfigs}, + frame_support::{assert_noop, assert_ok, dispatch::GetDispatchInfo}, + sp_std::vec, +}; + +#[test] +fn config_sets_values_from_genesis() { + let custom_config = HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 40, + max_orchestrator_collators: 40, + collators_per_container: 20, + full_rotation_period: 24, + ..Default::default() + }; + new_test_ext_with_genesis(custom_config.clone()).execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config(), custom_config); + }); +} + +#[test] +fn config_sets_default_values() { + let default_config = HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }; + new_test_ext().execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config(), default_config); + }); +} + +#[test] +fn config_set_value() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config().max_collators, 100); + assert_ok!( + Configuration::set_max_collators(RuntimeOrigin::root(), 50), + () + ); + + assert_eq!( + PendingConfigs::::get(), + vec![( + 2, + HostConfiguration { + max_collators: 50, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + } + )] + ); + + // The session delay is set to 2, and one session is 5 blocks, + // so the change should not happen until block 11 + assert_eq!(Configuration::config().max_collators, 100); + run_to_block(2); + assert_eq!(Configuration::config().max_collators, 100); + // First block of session 1 + run_to_block(6); + assert_eq!(Configuration::config().max_collators, 100); + run_to_block(10); + assert_eq!(Configuration::config().max_collators, 100); + // First block of session 2 + run_to_block(11); + assert_eq!(Configuration::config().max_collators, 50); + }); +} + +#[test] +fn config_set_full_rotation_period_to_zero_works() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config().full_rotation_period, 24); + assert_ok!( + Configuration::set_full_rotation_period(RuntimeOrigin::root(), 0), + () + ); + + assert_eq!( + PendingConfigs::::get(), + vec![( + 2, + HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 0, + ..Default::default() + } + )] + ); + + // The session delay is set to 2, and one session is 5 blocks, + // so the change should not happen until block 11 + assert_eq!(Configuration::config().full_rotation_period, 24); + run_to_block(2); + assert_eq!(Configuration::config().full_rotation_period, 24); + // First block of session 1 + run_to_block(6); + assert_eq!(Configuration::config().full_rotation_period, 24); + run_to_block(10); + assert_eq!(Configuration::config().full_rotation_period, 24); + // First block of session 2 + run_to_block(11); + assert_eq!(Configuration::config().full_rotation_period, 0); + }); +} + +#[test] +fn config_set_many_values_same_block() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config().max_collators, 100); + assert_eq!(Configuration::config().collators_per_container, 2); + assert_eq!(Configuration::config().min_orchestrator_collators, 2); + assert_ok!( + Configuration::set_max_collators(RuntimeOrigin::root(), 50), + () + ); + assert_ok!( + Configuration::set_min_orchestrator_collators(RuntimeOrigin::root(), 20), + () + ); + assert_ok!( + Configuration::set_collators_per_container(RuntimeOrigin::root(), 10), + () + ); + + assert_eq!( + PendingConfigs::::get(), + vec![( + 2, + HostConfiguration { + max_collators: 50, + min_orchestrator_collators: 20, + max_orchestrator_collators: 20, + collators_per_container: 10, + full_rotation_period: 24, + ..Default::default() + } + )] + ); + + // The session delay is set to 2, and one session is 5 blocks, + // so the change should not happen until block 11 + run_to_block(10); + assert_eq!(Configuration::config().max_collators, 100); + assert_eq!(Configuration::config().collators_per_container, 2); + assert_eq!(Configuration::config().min_orchestrator_collators, 2); + // First block of session 2 + run_to_block(11); + assert_eq!(Configuration::config().max_collators, 50); + assert_eq!(Configuration::config().collators_per_container, 10); + assert_eq!(Configuration::config().min_orchestrator_collators, 20); + }); +} + +#[test] +fn config_set_many_values_different_blocks() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config().max_collators, 100); + assert_eq!(Configuration::config().collators_per_container, 2); + assert_eq!(Configuration::config().min_orchestrator_collators, 2); + assert_ok!( + Configuration::set_max_collators(RuntimeOrigin::root(), 50), + () + ); + run_to_block(2); + assert_ok!( + Configuration::set_min_orchestrator_collators(RuntimeOrigin::root(), 20), + () + ); + run_to_block(3); + assert_ok!( + Configuration::set_collators_per_container(RuntimeOrigin::root(), 10), + () + ); + + assert_eq!( + PendingConfigs::::get(), + vec![( + 2, + HostConfiguration { + max_collators: 50, + min_orchestrator_collators: 20, + max_orchestrator_collators: 20, + collators_per_container: 10, + full_rotation_period: 24, + ..Default::default() + } + )] + ); + + // The session delay is set to 2, and one session is 5 blocks, + // so the change should not happen until block 11 + run_to_block(10); + assert_eq!(Configuration::config().max_collators, 100); + assert_eq!(Configuration::config().min_orchestrator_collators, 2); + assert_eq!(Configuration::config().collators_per_container, 2); + // First block of session 2 + run_to_block(11); + assert_eq!(Configuration::config().max_collators, 50); + assert_eq!(Configuration::config().min_orchestrator_collators, 20); + assert_eq!(Configuration::config().collators_per_container, 10); + }); +} + +#[test] +fn config_set_many_values_different_sessions() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config().max_collators, 100); + assert_eq!(Configuration::config().min_orchestrator_collators, 2); + assert_eq!(Configuration::config().collators_per_container, 2); + assert_ok!( + Configuration::set_max_collators(RuntimeOrigin::root(), 50), + () + ); + run_to_block(6); + assert_ok!( + Configuration::set_min_orchestrator_collators(RuntimeOrigin::root(), 20), + () + ); + assert_eq!(Configuration::config().max_collators, 100); + assert_eq!(Configuration::config().min_orchestrator_collators, 2); + assert_eq!(Configuration::config().collators_per_container, 2); + run_to_block(11); + assert_ok!( + Configuration::set_collators_per_container(RuntimeOrigin::root(), 10), + () + ); + + assert_eq!( + PendingConfigs::::get(), + vec![ + ( + 3, + HostConfiguration { + max_collators: 50, + min_orchestrator_collators: 20, + max_orchestrator_collators: 20, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + } + ), + ( + 4, + HostConfiguration { + max_collators: 50, + min_orchestrator_collators: 20, + max_orchestrator_collators: 20, + collators_per_container: 10, + full_rotation_period: 24, + ..Default::default() + } + ) + ] + ); + + assert_eq!(Configuration::config().max_collators, 50); + assert_eq!(Configuration::config().min_orchestrator_collators, 2); + assert_eq!(Configuration::config().collators_per_container, 2); + run_to_block(16); + assert_eq!(Configuration::config().max_collators, 50); + assert_eq!(Configuration::config().min_orchestrator_collators, 20); + assert_eq!(Configuration::config().collators_per_container, 2); + run_to_block(21); + assert_eq!(Configuration::config().max_collators, 50); + assert_eq!(Configuration::config().min_orchestrator_collators, 20); + assert_eq!(Configuration::config().collators_per_container, 10); + }); +} + +#[test] +fn config_cannot_set_invalid_values() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_noop!( + Configuration::set_max_collators(RuntimeOrigin::root(), 0), + Error::::InvalidNewValue + ); + assert_noop!( + Configuration::set_min_orchestrator_collators(RuntimeOrigin::root(), 0), + Error::::InvalidNewValue + ); + assert_noop!( + Configuration::set_max_orchestrator_collators(RuntimeOrigin::root(), 0), + Error::::InvalidNewValue + ); + }); +} + +#[test] +fn weights_assigned_to_extrinsics_are_correct() { + new_test_ext().execute_with(|| { + assert_eq!( + crate::Call::::set_max_collators { new: 1u32 } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::set_config_with_u32() + ); + + assert_eq!( + crate::Call::::set_min_orchestrator_collators { new: 1u32 } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::set_config_with_u32() + ); + + assert_eq!( + crate::Call::::set_collators_per_container { new: 1u32 } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::set_config_with_u32() + ); + + assert_eq!( + crate::Call::::set_max_orchestrator_collators { new: 1u32 } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::set_config_with_u32() + ); + }); +} + +#[test] +fn set_max_collators_below_min_orch_collators_errors() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config().max_collators, 100); + + // set max_collators to 1 + assert_noop!( + Configuration::set_max_collators(RuntimeOrigin::root(), 1), + Error::::InvalidNewValue + ); + }); +} + +#[test] +fn set_max_collators_below_min_orch_collators_errors_reverse() { + new_test_ext_with_genesis(HostConfiguration { + max_collators: 100, + min_orchestrator_collators: 2, + max_orchestrator_collators: 5, + collators_per_container: 2, + full_rotation_period: 24, + ..Default::default() + }) + .execute_with(|| { + run_to_block(1); + assert_eq!(Configuration::config().max_collators, 100); + + // set max_collators to 1 + assert_noop!( + Configuration::set_min_orchestrator_collators(RuntimeOrigin::root(), 101), + Error::::InvalidNewValue + ); + }); +} diff --git a/pallets/configuration/src/weights.rs b/pallets/configuration/src/weights.rs new file mode 100644 index 0000000..d296423 --- /dev/null +++ b/pallets/configuration/src/weights.rs @@ -0,0 +1,99 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_configuration +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `tomasz-XPS-15-9520`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_configuration +// --extrinsic +// set_config_with_u32 +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_configuration. +pub trait WeightInfo { + fn set_config_with_u32() -> Weight; +} + +/// Weights for pallet_configuration using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Configuration PendingConfigs (r:1 w:1) + /// Proof Skipped: Configuration PendingConfigs (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Configuration ActiveConfig (r:1 w:0) + /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Configuration BypassConsistencyCheck (r:1 w:0) + /// Proof Skipped: Configuration BypassConsistencyCheck (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + fn set_config_with_u32() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6948` + // Minimum execution time: 9_507_000 picoseconds. + Weight::from_parts(9_924_000, 6948) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Configuration PendingConfigs (r:1 w:1) + /// Proof Skipped: Configuration PendingConfigs (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Configuration ActiveConfig (r:1 w:0) + /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Configuration BypassConsistencyCheck (r:1 w:0) + /// Proof Skipped: Configuration BypassConsistencyCheck (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + fn set_config_with_u32() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6948` + // Minimum execution time: 9_507_000 picoseconds. + Weight::from_parts(9_924_000, 6948) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/data-preservers/Cargo.toml b/pallets/data-preservers/Cargo.toml new file mode 100644 index 0000000..54c0604 --- /dev/null +++ b/pallets/data-preservers/Cargo.toml @@ -0,0 +1,79 @@ +[package] +name = "pallet-data-preservers" +authors = { workspace = true } +description = "Allows container chains to select data preservers" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] + +dp-core = { workspace = true } +log = { workspace = true } +serde = { workspace = true, optional = true } +tp-traits = { workspace = true } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } + +# Nimbus +nimbus-primitives = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +bounded-collections = { workspace = true } +num-traits = { workspace = true } +pallet-balances = { workspace = true, features = [ "std" ] } +similar-asserts = { workspace = true } +sp-io = { workspace = true, features = [ "std" ] } + +[features] +default = [ "std" ] +std = [ + "bounded-collections/std", + "dp-core/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "nimbus-primitives/std", + "pallet-balances/std", + "parity-scale-codec/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "nimbus-primitives/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "nimbus-primitives/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/data-preservers/src/benchmarks.rs b/pallets/data-preservers/src/benchmarks.rs new file mode 100644 index 0000000..6a07dd3 --- /dev/null +++ b/pallets/data-preservers/src/benchmarks.rs @@ -0,0 +1,59 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#![cfg(feature = "runtime-benchmarks")] + +//! Benchmarking +use { + crate::{Call, Config, Pallet}, + frame_benchmarking::v2::*, + frame_support::{ + traits::{EnsureOriginWithArg, OriginTrait}, + BoundedVec, + }, + frame_system::RawOrigin, + sp_std::vec, + tp_traits::ParaId, +}; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn set_boot_nodes(x: Linear<1, 200>, y: Linear<1, 10>) { + // x: url len, y: num boot_nodes + let boot_nodes = BoundedVec::try_from(vec![ + BoundedVec::try_from(vec![b'A'; x as usize]) + .unwrap(); + y as usize + ]) + .unwrap(); + let para_id = ParaId::from(2); + let origin = T::SetBootNodesOrigin::try_successful_origin(¶_id) + .expect("failed to create SetBootNodesOrigin"); + // Worst case is when caller is not root + let raw_origin = origin.as_system_ref(); + assert!(matches!(raw_origin, Some(RawOrigin::Signed(..)))); + + #[extrinsic_call] + Pallet::::set_boot_nodes(origin as T::RuntimeOrigin, para_id, boot_nodes.clone()); + + assert_eq!(Pallet::::boot_nodes(para_id), boot_nodes); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/data-preservers/src/lib.rs b/pallets/data-preservers/src/lib.rs new file mode 100644 index 0000000..5626c39 --- /dev/null +++ b/pallets/data-preservers/src/lib.rs @@ -0,0 +1,172 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Data Preservers Pallet +//! +//! This pallet allows container chains to select data preservers. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(any(test, feature = "runtime-benchmarks"))] +mod benchmarks; +pub mod weights; +pub use weights::WeightInfo; + +use { + dp_core::ParaId, + frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{Balanced, Inspect}, + EnsureOriginWithArg, + }, + DefaultNoBound, + }, + frame_system::pallet_prelude::*, + sp_runtime::traits::Get, + sp_std::vec::Vec, +}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig { + /// Para ids + pub para_id_boot_nodes: Vec<(ParaId, Vec>)>, + pub _phantom: PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + // Sort para ids and detect duplicates, but do it using a vector of + // references to avoid cloning the boot nodes. + let mut para_ids: Vec<&_> = self.para_id_boot_nodes.iter().collect(); + para_ids.sort_by(|a, b| a.0.cmp(&b.0)); + para_ids.dedup_by(|a, b| { + if a.0 == b.0 { + panic!("Duplicate para_id: {}", u32::from(a.0)); + } else { + false + } + }); + + for (para_id, boot_nodes) in para_ids { + let boot_nodes: Vec<_> = boot_nodes + .iter() + .map(|x| BoundedVec::try_from(x.clone()).expect("boot node url too long")) + .collect(); + let boot_nodes = BoundedVec::try_from(boot_nodes).expect("too many boot nodes"); + >::insert(para_id, boot_nodes); + } + } + } + + /// Data preservers pallet. + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type Currency: Inspect + Balanced; + // Who can call set_boot_nodes? + type SetBootNodesOrigin: EnsureOriginWithArg; + + #[pallet::constant] + type MaxBootNodes: Get; + #[pallet::constant] + type MaxBootNodeUrlLen: Get; + + type WeightInfo: WeightInfo; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// The list of boot_nodes changed. + BootNodesChanged { para_id: ParaId }, + } + + #[pallet::error] + pub enum Error { + /// This container chain does not have any boot nodes + NoBootNodes, + } + + #[pallet::storage] + #[pallet::getter(fn boot_nodes)] + pub type BootNodes = StorageMap< + _, + Blake2_128Concat, + ParaId, + BoundedVec, T::MaxBootNodes>, + ValueQuery, + >; + + #[pallet::call] + impl Pallet { + /// Set boot_nodes for this para id + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::set_boot_nodes( + T::MaxBootNodeUrlLen::get(), + boot_nodes.len() as u32, + ))] + pub fn set_boot_nodes( + origin: OriginFor, + para_id: ParaId, + boot_nodes: BoundedVec, T::MaxBootNodes>, + ) -> DispatchResult { + T::SetBootNodesOrigin::ensure_origin(origin, ¶_id)?; + + BootNodes::::insert(para_id, boot_nodes); + + Self::deposit_event(Event::BootNodesChanged { para_id }); + + Ok(()) + } + } + + impl Pallet { + /// Function that will be called when a container chain is deregistered. Cleans up all the storage related to this para_id. + /// Cannot fail. + pub fn para_deregistered(para_id: ParaId) { + BootNodes::::remove(para_id); + } + + pub fn check_valid_for_collating(para_id: ParaId) -> DispatchResult { + // To be able to call mark_valid_for_collating, a container chain must have bootnodes + if Pallet::::boot_nodes(para_id).len() > 0 { + Ok(()) + } else { + Err(Error::::NoBootNodes.into()) + } + } + } +} diff --git a/pallets/data-preservers/src/mock.rs b/pallets/data-preservers/src/mock.rs new file mode 100644 index 0000000..d4f99cf --- /dev/null +++ b/pallets/data-preservers/src/mock.rs @@ -0,0 +1,240 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{self as pallet_data_preservers}, + dp_core::ParaId, + frame_support::{ + pallet_prelude::*, + parameter_types, + traits::{ConstU64, EitherOfDiverse, EnsureOriginWithArg, Everything}, + }, + frame_system::{EnsureRoot, EnsureSigned, RawOrigin}, + sp_core::H256, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, Either, + }, + sp_std::collections::btree_map::BTreeMap, +}; + +type Block = frame_system::mocking::MockBlock; +pub type AccountId = u64; +pub type Balance = u128; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + DataPreservers: pallet_data_preservers, + MockData: mock_data, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type Block = Block; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; + type RuntimeTask = (); +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} + +impl pallet_balances::Config for Test { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); + type MaxHolds = ConstU32<5>; + type WeightInfo = (); +} + +// Pallet to provide some mock data, used to test +#[frame_support::pallet] +pub mod mock_data { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn mock)] + pub(super) type Mock = StorageValue<_, Mocks, ValueQuery>; + + impl Pallet { + pub fn get() -> Mocks { + Mock::::get() + } + pub fn mutate(f: F) -> R + where + F: FnOnce(&mut Mocks) -> R, + { + Mock::::mutate(f) + } + } +} + +impl mock_data::Config for Test {} + +#[derive(Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Mocks { + /// List of container chains, with the corresponding "manager" account. + /// In dancebox, the manager is the one who put the deposit in pallet_registrar. + /// The manager can be None if the chain was registered by root, or in genesis. + pub container_chain_managers: BTreeMap>, +} + +impl Default for Mocks { + fn default() -> Self { + Self { + container_chain_managers: BTreeMap::from_iter([(ParaId::from(1001), None)]), + } + } +} + +pub struct MockContainerChainManagerOrRootOrigin { + // Configurable root origin + container_chain_manager_origin: PhantomData, + _phantom: PhantomData, +} + +impl EnsureOriginWithArg + for MockContainerChainManagerOrRootOrigin +where + T: crate::Config, + RootOrigin: EnsureOriginWithArg, + O: From>, + Result, O>: From, + u64: From, + T::AccountId: From, + O: Clone, +{ + type Success = Either>::Success>; + + fn try_origin(o: O, para_id: &ParaId) -> Result { + let origin = EitherOfDiverse::, RootOrigin>::try_origin( + o.clone(), + para_id, + )?; + + if let Either::Left(signed_account) = &origin { + // This check will only pass if both are true: + // * The para_id has a deposit in pallet_registrar + // * The deposit creator is the signed_account + MockData::get() + .container_chain_managers + .get(para_id) + .and_then(|inner| *inner) + .and_then(|manager| { + if manager != u64::from(signed_account.clone()) { + None + } else { + Some(()) + } + }) + .ok_or(o)?; + } + + Ok(origin) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(para_id: &ParaId) -> Result { + // Return container chain manager, or register container chain as ALICE if it does not exist + MockData::mutate(|m| { + m.container_chain_managers + .entry(*para_id) + .or_insert_with(move || { + const ALICE: u64 = 1; + + Some(ALICE) + }); + }); + + // This panics if the container chain was registered by root (None) + let o = MockData::get() + .container_chain_managers + .get(para_id) + .unwrap() + .unwrap(); + + Ok(O::from(RawOrigin::Signed(o.into()))) + } +} + +impl pallet_data_preservers::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type SetBootNodesOrigin = MockContainerChainManagerOrRootOrigin>; + type MaxBootNodes = ConstU32<10>; + type MaxBootNodeUrlLen = ConstU32<200>; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + let balances = vec![(0, 10_000)]; + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() +} diff --git a/pallets/data-preservers/src/tests.rs b/pallets/data-preservers/src/tests.rs new file mode 100644 index 0000000..bc738b2 --- /dev/null +++ b/pallets/data-preservers/src/tests.rs @@ -0,0 +1,198 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{mock::*, *}, + frame_support::{assert_noop, assert_ok, pallet_prelude::*}, +}; + +const ALICE: u64 = 1; +const BOB: u64 = 2; + +#[test] +fn set_boot_nodes_bad_origin() { + new_test_ext().execute_with(|| { + // Para 1001 has no manager, Alice cannot set boot nodes + assert_noop!(DataPreservers::set_boot_nodes( + RuntimeOrigin::signed(ALICE), + 1001.into(), + vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9".to_vec().try_into().unwrap() + ].try_into().unwrap() + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn set_boot_nodes_by_root_no_manager() { + new_test_ext().execute_with(|| { + // Para 1001 has no manager, root can set boot nodes + let boot_nodes: BoundedVec, _> = vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9" + .to_vec() + .try_into() + .unwrap(), + ] + .try_into() + .unwrap(); + assert_ok!(DataPreservers::set_boot_nodes( + RuntimeOrigin::root(), + 1001.into(), + boot_nodes.clone(), + )); + assert_eq!(DataPreservers::boot_nodes(ParaId::from(1001)), boot_nodes); + }); +} + +#[test] +fn set_boot_nodes_by_root_with_manager() { + new_test_ext().execute_with(|| { + // Set ALICE as manager of para 1002 + MockData::mutate(|m| { + m.container_chain_managers.insert(1002.into(), Some(ALICE)); + }); + // Root can set bootnodes + let boot_nodes: BoundedVec, _> = vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9" + .to_vec() + .try_into() + .unwrap(), + ] + .try_into() + .unwrap(); + assert_ok!(DataPreservers::set_boot_nodes( + RuntimeOrigin::root(), + 1002.into(), + boot_nodes.clone() + )); + assert_eq!(DataPreservers::boot_nodes(ParaId::from(1002)), boot_nodes); + }); +} + +#[test] +fn set_boot_nodes_by_para_id_registrar() { + new_test_ext().execute_with(|| { + // Set ALICE as manager of para 1002 + MockData::mutate(|m| { + m.container_chain_managers.insert(1002.into(), Some(ALICE)); + }); + // Alice can set bootnodes + let boot_nodes: BoundedVec, _> = vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9" + .to_vec() + .try_into() + .unwrap(), + ] + .try_into() + .unwrap(); + assert_ok!(DataPreservers::set_boot_nodes( + RuntimeOrigin::signed(ALICE), + 1002.into(), + boot_nodes.clone(), + )); + assert_eq!(DataPreservers::boot_nodes(ParaId::from(1002)), boot_nodes); + }); +} + +#[test] +fn set_boot_nodes_by_invalid_user_no_manager() { + new_test_ext().execute_with(|| { + // Para 1001 has no manager + MockData::mutate(|m| { + m.container_chain_managers.insert(1002.into(), Some(ALICE)); + }); + // Bob cannot set the bootnodes + assert_noop!(DataPreservers::set_boot_nodes( + RuntimeOrigin::signed(BOB), + 1001.into(), + vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9".to_vec().try_into().unwrap() + ].try_into().unwrap() + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn set_boot_nodes_by_invalid_user() { + new_test_ext().execute_with(|| { + // Set ALICE as manager of para 1002 + MockData::mutate(|m| { + m.container_chain_managers.insert(1002.into(), Some(ALICE)); + }); + // Bob cannot set the bootnodes + assert_noop!(DataPreservers::set_boot_nodes( + RuntimeOrigin::signed(BOB), + 1002.into(), + vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9".to_vec().try_into().unwrap() + ].try_into().unwrap() + ), + DispatchError::BadOrigin + ); + + assert_noop!(DataPreservers::set_boot_nodes( + RuntimeOrigin::signed(BOB), + 1003.into(), + vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9".to_vec().try_into().unwrap() + ].try_into().unwrap() + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn set_boot_nodes_by_invalid_user_bad_para_id() { + new_test_ext().execute_with(|| { + // Para 1003 does not exist, only root can set bootnodes + assert_noop!(DataPreservers::set_boot_nodes( + RuntimeOrigin::signed(BOB), + 1003.into(), + vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9".to_vec().try_into().unwrap() + ].try_into().unwrap() + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn set_boot_nodes_bad_para_id() { + // Para 1003 does not exist, only root can set bootnodes + // This is allowed in case we want to set bootnodes before registering the chain + new_test_ext().execute_with(|| { + let boot_nodes: BoundedVec, _> = vec![ + b"/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9" + .to_vec() + .try_into() + .unwrap(), + ] + .try_into() + .unwrap(); + assert_ok!(DataPreservers::set_boot_nodes( + RuntimeOrigin::root(), + 1003.into(), + boot_nodes.clone(), + )); + assert_eq!(DataPreservers::boot_nodes(ParaId::from(1003)), boot_nodes); + }); +} diff --git a/pallets/data-preservers/src/weights.rs b/pallets/data-preservers/src/weights.rs new file mode 100644 index 0000000..b93d57d --- /dev/null +++ b/pallets/data-preservers/src/weights.rs @@ -0,0 +1,103 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_data_preservers +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `tomasz-XPS-15-9520`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_data_preservers +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_data_preservers. +pub trait WeightInfo { + fn set_boot_nodes(x: u32, y: u32, ) -> Weight; +} + +/// Weights for pallet_data_preservers using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:0 w:1) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + /// The range of component `y` is `[1, 10]`. + fn set_boot_nodes(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 10_703_000 picoseconds. + Weight::from_parts(9_788_229, 3660) + // Standard Error: 170 + .saturating_add(Weight::from_parts(7_964, 0).saturating_mul(x.into())) + // Standard Error: 3_552 + .saturating_add(Weight::from_parts(334_296, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:0 w:1) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + /// The range of component `y` is `[1, 10]`. + fn set_boot_nodes(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 10_703_000 picoseconds. + Weight::from_parts(9_788_229, 3660) + // Standard Error: 170 + .saturating_add(Weight::from_parts(7_964, 0).saturating_mul(x.into())) + // Standard Error: 3_552 + .saturating_add(Weight::from_parts(334_296, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/department-funding/Cargo.toml b/pallets/department-funding/Cargo.toml deleted file mode 100644 index 4f57ef7..0000000 --- a/pallets/department-funding/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -[package] -name = "department-funding" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-timestamp = { git = 'https://github.com/paritytech/substrate', branch = "polkadot-v0.9.42", default-features = false } -pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-support = { default-features = false, path = '../support' } -shared-storage = { default-features = false, path="../shared-storage"} -shared-storage-link = { default-features = false, path="../../traits/shared-storage-link"} -schelling-game-shared = {default-features = false, path = "../schelling-game-shared"} -schelling-game-shared-link = {default-features = false, path = "../../traits/schelling-game-shared-link"} -sortition-sum-game = {default-features = false, path="../sortition-sum-game"} - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support-test = { version = "3.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "pallet-timestamp/std", - "pallet-balances/std", - "pallet-support/std", - "shared-storage/std", - "schelling-game-shared/std", - "sortition-sum-game/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/department-funding/department-funding-rpc/Cargo.toml b/pallets/department-funding/department-funding-rpc/Cargo.toml deleted file mode 100644 index 3b6101a..0000000 --- a/pallets/department-funding/department-funding-rpc/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "department-funding-rpc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } -sc-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-api = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -department-funding-runtime-api = { default-features= false, path="../department-funding-runtime-api"} \ No newline at end of file diff --git a/pallets/department-funding/department-funding-runtime-api/Cargo.toml b/pallets/department-funding/department-funding-runtime-api/Cargo.toml deleted file mode 100644 index feb6e7e..0000000 --- a/pallets/department-funding/department-funding-runtime-api/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "department-funding-runtime-api" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -sp-api = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42"} - -[features] -default = ["std"] -std = [ - "sp-api/std", - "frame-support/std", -] \ No newline at end of file diff --git a/pallets/department-funding/department-funding-runtime-api/src/lib.rs b/pallets/department-funding/department-funding-runtime-api/src/lib.rs deleted file mode 100644 index a1084ce..0000000 --- a/pallets/department-funding/department-funding-runtime-api/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -// use frame_support::sp_std::{vec::Vec}; -// or -use frame_support::sp_std::prelude::*; -use sp_api::codec::Codec; - -type DepartmentRequiredFundId = u64; - -sp_api::decl_runtime_apis! { - pub trait DepartmentFundingApi where AccountId: Codec { - - fn get_evidence_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; - fn get_staking_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; - fn get_drawing_period_end(department_required_fund_id: DepartmentRequiredFundId) -> (u64, u64, bool); - fn get_commit_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; - fn get_vote_period_end_block(department_required_fund_id: DepartmentRequiredFundId) -> Option; - fn selected_as_juror(department_required_fund_id: DepartmentRequiredFundId, who: AccountId) -> bool; - } -} diff --git a/pallets/department-funding/src/extras.rs b/pallets/department-funding/src/extras.rs deleted file mode 100644 index 6c37eb8..0000000 --- a/pallets/department-funding/src/extras.rs +++ /dev/null @@ -1,283 +0,0 @@ -use crate::*; -use types::{TIME_FOR_STAKING_FUNDING_STATUS_FAILED, TIME_FOR_STAKING_FUNDING_STATUS_PASSED}; - -impl DepartmentRequiredFund { - pub fn new( - department_required_fund_id: DepartmentRequiredFundId, - department_id: DepartmentId, - content: Content, - tipping_name: TippingName, - funding_needed: BalanceOf, - creator: T::AccountId, - ) -> Self { - DepartmentRequiredFund { - created: new_who_and_when::(creator.clone()), - department_required_fund_id, - content, - department_id, - tipping_name, - funding_needed, - creator, - } - } -} - -impl Pallet { - pub(super) fn get_phase_data() -> PhaseData { - T::SchellingGameSharedSource::create_phase_data(50, 5, 3, 100, (100, 100)) - } - - pub fn ensure_validation_to_do( - department_required_fund_id: DepartmentRequiredFundId, - ) -> DispatchResult { - let bool_data = ValidateDepartmentRequiredFund::::get(department_required_fund_id); - ensure!(bool_data == true, Error::::ValidationForDepartmentRequiredFundIdIsOff); - - Ok(()) - } - - pub fn get_department_id_from_department_required_fund_id( - department_required_fund_id: DepartmentRequiredFundId, - ) -> Result { - let department_required_fund_option = - DepartmentRequiredFunds::::get(department_required_fund_id); - - match department_required_fund_option { - Some(department_required_fund) => Ok(department_required_fund.department_id), - None => Err(Error::::DepartmentRequiredFundDontExits)?, - } - } - - pub fn ensure_can_stake_using_status( - department_id: DepartmentId, - ) -> Result, FundingStatus>, DispatchError> { - let now = >::block_number(); - let department_funding_status = - DepartmentFundingStatus { block_number: now, status: FundingStatus::Processing }; - let department_status_option = - DepartmentFundingStatusForDepartmentId::::get(department_id); - match department_status_option { - Some(department_status) => { - let funding_status = department_status.status; - if funding_status == FundingStatus::Processing { - Err(Error::::FundingStatusProcessing.into()) - } else if funding_status == FundingStatus::Failed { - // else check 3 month if status faild or 6 months if status success to reapply for funding - let status_failed_time = TIME_FOR_STAKING_FUNDING_STATUS_FAILED; - let status_failed_time_block = Self::u64_to_block_saturated(status_failed_time); - let funding_status_block = department_status.block_number; - let time = now.checked_sub(&funding_status_block).expect("Overflow"); - if time >= status_failed_time_block { - Ok(department_funding_status) - } else { - Err(Error::::ReapplicationTimeNotReached.into()) - } - } else if funding_status == FundingStatus::Success { - let status_success_time = TIME_FOR_STAKING_FUNDING_STATUS_PASSED; - let status_success_time_block = - Self::u64_to_block_saturated(status_success_time); - let funding_status_block = department_status.block_number; - let time = now.checked_sub(&funding_status_block).expect("Overflow"); - if time >= status_success_time_block { - Ok(department_funding_status) - } else { - Err(Error::::ReapplicationTimeNotReached.into()) - } - } else { - Err(Error::::ConditionDontMatch.into()) - } - }, - None => Ok(department_funding_status), - } - } - - // pub fn ensure_user_is_project_creator_and_project_exists( - // project_id: ProjectId, - // user: T::AccountId, - // ) -> DispatchResult { - // let project_option: Option> = Projects::get(project_id); - // match project_option { - // Some(project) => { - // let creator = project.creator; - // ensure!(creator == user, Error::::ProjectCreatorDontMatch); - // }, - // None => Err(Error::::ProjectDontExists)?, - // } - - // Ok(()) - // } - - // pub fn ensure_staking_period_set_once_project_id(project_id: ProjectId) -> DispatchResult { - // let block_number_option = >::get(project_id); - // match block_number_option { - // Some(_block) => Err(Error::::ProjectIdStakingPeriodAlreadySet)?, - // None => Ok(()), - // } - // } - - pub fn get_block_number_of_schelling_game( - department_required_fund_id: DepartmentRequiredFundId, - ) -> Result, DispatchError> { - let block_number_option = - >::get(department_required_fund_id); - let block_number = match block_number_option { - Some(block_number) => block_number, - None => Err(Error::::BlockDepartmentRequiredFundIdNotExists)?, - }; - Ok(block_number) - } - - pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { - input.saturated_into::>() - } - - pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { - input.saturated_into::>() - } - - pub(super) fn value_of_tipping_name(tipping: TippingName) -> TippingValue> { - match tipping { - TippingName::SmallTipper => TippingValue { - max_tipping_value: 10_000u64.saturated_into::>(), - stake_required: 10u64.saturated_into::>(), - }, - TippingName::BigTipper => TippingValue { - max_tipping_value: 100_000u64.saturated_into::>(), - stake_required: 50u64.saturated_into::>(), - }, - TippingName::SmallSpender => TippingValue { - max_tipping_value: 1_000_000u64.saturated_into::>(), - stake_required: 100u64.saturated_into::>(), - }, - TippingName::MediumSpender => TippingValue { - max_tipping_value: 10_000_000u64.saturated_into::>(), - stake_required: 200u64.saturated_into::>(), - }, - TippingName::BigSpender => TippingValue { - max_tipping_value: 100_000_000u64.saturated_into::>(), - stake_required: 500u64.saturated_into::>(), - }, - } - } - - pub fn get_evidence_period_end_block( - department_required_fund_id: DepartmentRequiredFundId, - ) -> Option { - let now = >::block_number(); - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - // End block code start - - pub fn get_staking_period_end_block( - department_required_fund_id: DepartmentRequiredFundId, - ) -> Option { - let now = >::block_number(); - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_drawing_period_end( - department_required_fund_id: DepartmentRequiredFundId, - ) -> (u64, u64, bool) { - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - let phase_data = Self::get_phase_data(); - - let result = - T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); - result - } - - pub fn get_commit_period_end_block( - department_required_fund_id: DepartmentRequiredFundId, - ) -> Option { - let now = >::block_number(); - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_vote_period_end_block( - department_required_fund_id: DepartmentRequiredFundId, - ) -> Option { - let now = >::block_number(); - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn selected_as_juror( - department_required_fund_id: DepartmentRequiredFundId, - who: T::AccountId, - ) -> bool { - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id).unwrap(); - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); - result - } - - // End block code end -} diff --git a/pallets/department-funding/src/lib.rs b/pallets/department-funding/src/lib.rs deleted file mode 100644 index 5f6c65b..0000000 --- a/pallets/department-funding/src/lib.rs +++ /dev/null @@ -1,449 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -// One can enhance validation measures by increasing staking power for local residents or individuals with positive externalities—those who contribute to the network for a good cause. -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -mod extras; -mod types; - -use frame_support::sp_runtime::traits::{CheckedAdd, CheckedSub}; -use frame_support::sp_runtime::SaturatedConversion; -use frame_support::sp_std::prelude::*; -use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - ensure, -}; -use frame_support::{ - traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, - PalletId, -}; -use pallet_support::{ - ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, PostId, - WhoAndWhen, WhoAndWhenOf, -}; -use schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType}; -use schelling_game_shared_link::SchellingGameSharedLink; -use shared_storage_link::SharedStorageLink; -use sortition_sum_game::types::SumTreeName; -pub use types::DEPARTMENT_REQUIRED_FUND_ID; -use types::{ - DepartmentFundingStatus, DepartmentRequiredFund, FundingStatus, TippingName, TippingValue, -}; - -type AccountIdOf = ::AccountId; -type BalanceOf = <::Currency as Currency>>::Balance; -pub type BlockNumberOf = ::BlockNumber; -pub type SumTreeNameType = SumTreeName, BlockNumberOf>; -type DepartmentId = u64; -type DepartmentRequiredFundId = u64; - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: - frame_system::Config + schelling_game_shared::Config + pallet_timestamp::Config - { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - - type SharedStorageSource: SharedStorageLink>; - type SchellingGameSharedSource: SchellingGameSharedLink< - SumTreeName = SumTreeName, - SchellingGameType = SchellingGameType, - BlockNumber = Self::BlockNumber, - AccountId = AccountIdOf, - Balance = BalanceOf, - RangePoint = RangePoint, - Period = Period, - PhaseData = PhaseData, - >; - type Currency: ReservableCurrency; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::type_value] - pub fn MinimumDepartmentStake() -> BalanceOf { - 10000u128.saturated_into::>() - } - - #[pallet::type_value] - pub fn DefaultForNextDepartmentRequiredFundId() -> DepartmentRequiredFundId { - DEPARTMENT_REQUIRED_FUND_ID - } - - #[pallet::storage] - #[pallet::getter(fn next_department_required_fund_id)] - pub type NextDepartmentRequiredFundId = StorageValue< - _, - DepartmentRequiredFundId, - ValueQuery, - DefaultForNextDepartmentRequiredFundId, - >; - - #[pallet::storage] - #[pallet::getter(fn get_department_required_funds)] - pub type DepartmentRequiredFunds = - StorageMap<_, Blake2_128Concat, DepartmentRequiredFundId, DepartmentRequiredFund>; - - #[pallet::storage] - #[pallet::getter(fn validate_positive_externality)] - pub type ValidateDepartmentRequiredFund = - StorageMap<_, Twox64Concat, DepartmentRequiredFundId, bool, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn validation_block)] - pub type ValidationBlock = - StorageMap<_, Blake2_128Concat, DepartmentRequiredFundId, BlockNumberOf>; - - #[pallet::storage] - #[pallet::getter(fn department_funding_status)] - pub type DepartmentFundingStatusForDepartmentId = StorageMap< - _, - Blake2_128Concat, - DepartmentId, - DepartmentFundingStatus, FundingStatus>, - >; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { something: u32, who: T::AccountId }, - DepartmentFundCreated { - account: T::AccountId, - department_required_fund_id: DepartmentRequiredFundId, - }, - StakingPeriodStarted { - department_required_fund_id: DepartmentRequiredFundId, - block_number: BlockNumberOf, - }, - ApplyJurors { - department_required_fund_id: DepartmentRequiredFundId, - block_number: BlockNumberOf, - account: T::AccountId, - }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - LessThanMinStake, - CannotStakeNow, - ChoiceOutOfRange, - FundingMoreThanTippingValue, - DepartmentRequiredFundDontExits, - BlockDepartmentRequiredFundIdNotExists, - ValidationForDepartmentRequiredFundIdIsOff, - FundingStatusProcessing, - ReapplicationTimeNotReached, - ConditionDontMatch, - } - - // Check deparment exists, it will done using loose coupling - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(0)] - pub fn create_department_required_fund( - origin: OriginFor, - department_id: DepartmentId, - content: Content, - tipping_name: TippingName, - funding_needed: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let tipping_value = Self::value_of_tipping_name(tipping_name); - let max_tipping_value = tipping_value.max_tipping_value; - let stake_required = tipping_value.stake_required; - let new_department_fund_id = Self::next_department_required_fund_id(); - let new_department_fund: DepartmentRequiredFund = DepartmentRequiredFund::new( - new_department_fund_id, - department_id, - content, - tipping_name, - funding_needed, - who.clone(), - ); - ensure!(funding_needed <= max_tipping_value, Error::::FundingMoreThanTippingValue); - // Check user has done kyc - let _ = ::Currency::withdraw( - &who, - stake_required, - WithdrawReasons::TRANSFER, - ExistenceRequirement::AllowDeath, - )?; - DepartmentRequiredFunds::insert(new_department_fund_id, new_department_fund); - NextDepartmentRequiredFundId::::mutate(|n| { - *n += 1; - }); - - Self::deposit_event(Event::DepartmentFundCreated { - account: who, - department_required_fund_id: new_department_fund_id, - }); - Ok(()) - } - - // Check update and discussion time over, only project creator can apply staking period - #[pallet::call_index(1)] - #[pallet::weight(0)] - pub fn apply_staking_period( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - Self::ensure_validation_to_do(department_required_fund_id)?; - let department_id = Self::get_department_id_from_department_required_fund_id( - department_required_fund_id, - )?; - let department_funding_status = Self::ensure_can_stake_using_status(department_id)?; - DepartmentFundingStatusForDepartmentId::::insert( - department_id, - department_funding_status, - ); - let now = >::block_number(); - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: now.clone(), - }; - ValidationBlock::::insert( - department_required_fund_id, - now.clone(), - ); - T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now.clone())?; - T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; - - Self::deposit_event(Event::StakingPeriodStarted { - department_required_fund_id, - block_number: now, - }); - - Ok(()) - } - - // // Check update and discussion time over, only project creator can apply staking period - // #[pallet::call_index(1)] - // #[pallet::weight(0)] - // pub fn apply_staking_period(origin: OriginFor, department_required_fund_id: DepartmentRequiredFundId) -> DispatchResult { - // let who = ensure_signed(origin)?; - - // Self::ensure_user_is_project_creator_and_project_exists(project_id, who)?; - // Self::ensure_staking_period_set_once_project_id(project_id)?; - - // let now = >::block_number(); - - // let key = SumTreeName::ProjectTips { project_id, block_number: now.clone() }; - - // >::insert(project_id, now.clone()); - // // check what if called again, its done with `ensure_staking_period_set_once_project_id` - // T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now.clone())?; - // T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; - - // Self::deposit_event(Event::StakinPeriodStarted { project_id, block_number: now }); - - // Ok(()) - // } - - #[pallet::call_index(2)] - #[pallet::weight(0)] - pub fn apply_jurors( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - stake: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id)?; - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::apply_jurors_helper_link( - key, - phase_data, - who.clone(), - stake, - )?; - Self::deposit_event(Event::ApplyJurors { - department_required_fund_id, - block_number, - account: who, - }); - - Ok(()) - } - - #[pallet::call_index(3)] - #[pallet::weight(0)] - pub fn pass_period( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id)?; - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let now = >::block_number(); - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; - Ok(()) - } - - #[pallet::call_index(4)] - #[pallet::weight(0)] - pub fn draw_jurors( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - iterations: u64, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id)?; - - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; - - Ok(()) - } - - // Unstaking - // Stop drawn juror to unstake ✔️ - #[pallet::call_index(5)] - #[pallet::weight(0)] - pub fn unstaking( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id)?; - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; - Ok(()) - } - - #[pallet::call_index(6)] - #[pallet::weight(0)] - pub fn commit_vote( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - vote_commit: [u8; 32], - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id)?; - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - T::SchellingGameSharedSource::commit_vote_helper_link(key, who, vote_commit)?; - Ok(()) - } - - #[pallet::call_index(7)] - #[pallet::weight(0)] - pub fn reveal_vote( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - choice: u128, - salt: Vec, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id)?; - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - T::SchellingGameSharedSource::reveal_vote_two_choice_helper_link( - key, who, choice, salt, - )?; - Ok(()) - } - - #[pallet::call_index(8)] - #[pallet::weight(0)] - pub fn get_incentives( - origin: OriginFor, - department_required_fund_id: DepartmentRequiredFundId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = - Self::get_block_number_of_schelling_game(department_required_fund_id)?; - let key = SumTreeName::DepartmentRequiredFund { - department_required_fund_id, - block_number: block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( - key, phase_data, who, - )?; - Ok(()) - } - } -} diff --git a/pallets/department-funding/src/mock.rs b/pallets/department-funding/src/mock.rs deleted file mode 100644 index 1021d9a..0000000 --- a/pallets/department-funding/src/mock.rs +++ /dev/null @@ -1,161 +0,0 @@ -use crate as pallet_template; -use frame_support::{ - parameter_types, - traits::{ConstU16, ConstU64, GenesisBuild}, -}; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; -use frame_support_test::TestRandomness; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - DepartmentFunding: pallet_template, - Balances: pallet_balances, - SharedStorage:shared_storage, - SchellingGameShared: schelling_game_shared, - SortitionSumGame: sortition_sum_game, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - type AccountData = pallet_balances::AccountData; // New code -} - -parameter_types! { - pub const MinimumPeriod: u64 = 5; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -impl shared_storage::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type SharedStorageSource = SharedStorage; - type Currency = Balances; // New code - type SchellingGameSharedSource = SchellingGameShared; -} - -impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU64<1>; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type MaxHolds = (); - type HoldIdentifier = (); - type AccountStore = System; -} - -impl schelling_game_shared::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Currency = Balances; // New code - type RandomnessSource = TestRandomness; - type Slash = (); - type Reward = (); - type SortitionSumGameSource = SortitionSumGame; -} - -impl sortition_sum_game::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 100000), - (2, 200000), - (3, 300000), - (4, 300000), - (5, 300000), - (6, 300000), - (7, 300000), - (8, 300000), - (9, 300000), - (10, 300000), - (11, 300000), - (12, 300000), - (13, 300000), - (14, 300000), - (15, 300000), - (16, 300000), - (17, 300000), - (18, 300000), - (19, 300000), - (20, 300000), - (21, 300000), - (22, 300000), - (23, 300000), - (24, 300000), - (25, 300000), - (26, 300000), - (27, 300000), - (28, 300000), - (29, 300000), - (30, 300000), - (31, 300000), - (32, 300000), - (33, 300000), - (34, 300000), - (35, 300000), - ], - } // new code - .assimilate_storage(&mut t) - .unwrap(); - shared_storage::GenesisConfig:: { approved_citizen_address: vec![1, 2] } - .assimilate_storage(&mut t) - .unwrap(); - t.into() -} diff --git a/pallets/department-funding/src/tests.rs b/pallets/department-funding/src/tests.rs deleted file mode 100644 index b44278c..0000000 --- a/pallets/department-funding/src/tests.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::{mock::*, Error, Event}; -use frame_support::{assert_noop, assert_ok}; - -#[test] -fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - // Dispatch a signed extrinsic. - }); -} - -#[test] -fn correct_error_for_none_value() { - new_test_ext().execute_with(|| { - // Ensure the expected error is thrown when no value is present. - }); -} diff --git a/pallets/election/Cargo.toml b/pallets/election/Cargo.toml deleted file mode 100644 index 85facf0..0000000 --- a/pallets/election/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "pallet-election" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-npos-elections = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -log = { default-features= false, version="0.4"} - - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support-test = { version = "3.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "pallet-balances/std", - "sp-npos-elections/std", - "sp-runtime/std", - "log/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/election/election-rpc/Cargo.toml b/pallets/election/election-rpc/Cargo.toml deleted file mode 100644 index 62cfbfe..0000000 --- a/pallets/election/election-rpc/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "election-rpc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -jsonrpc-core = {version = "18.0.0", features = ["arbitrary_precision"]} -jsonrpc-core-client = "18.0" -jsonrpc-derive = "18.0" -sc-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-api = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -election-runtime-api ={ path="../election-runtime-api", default-features = false} - - -[features] -default = ["std"] -std = [ - "sp-api/std", - "sp-runtime/std", - "election-runtime-api/std" -] - diff --git a/pallets/election/src/benchmarking.rs b/pallets/election/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/election/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/inflation-rewards/Cargo.toml b/pallets/inflation-rewards/Cargo.toml new file mode 100644 index 0000000..4878b74 --- /dev/null +++ b/pallets/inflation-rewards/Cargo.toml @@ -0,0 +1,79 @@ +[package] +name = "pallet-inflation-rewards" +authors = { workspace = true } +description = "A pallet to handle token inflation and rewards" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] + +dp-core = { workspace = true } +log = { workspace = true } +serde = { workspace = true, optional = true } +tp-traits = { workspace = true } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } + +# Nimbus +nimbus-primitives = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +bounded-collections = { workspace = true } +num-traits = { workspace = true } +pallet-balances = { workspace = true, features = [ "std" ] } +similar-asserts = { workspace = true } +sp-io = { workspace = true, features = [ "std" ] } + +[features] +default = [ "std" ] +std = [ + "bounded-collections/std", + "dp-core/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "nimbus-primitives/std", + "pallet-balances/std", + "parity-scale-codec/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "nimbus-primitives/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "nimbus-primitives/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/inflation-rewards/src/lib.rs b/pallets/inflation-rewards/src/lib.rs new file mode 100644 index 0000000..3d5e48b --- /dev/null +++ b/pallets/inflation-rewards/src/lib.rs @@ -0,0 +1,285 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Inflation Rewards Pallet +//! +//! This pallet handle native token inflation and rewards distribution. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +use { + dp_core::{BlockNumber, ParaId}, + frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{Balanced, Credit, Inspect}, + tokens::{Fortitude, Precision, Preservation}, + Imbalance, OnUnbalanced, + }, + }, + frame_system::pallet_prelude::*, + sp_runtime::{ + traits::{Get, Saturating}, + Perbill, + }, + tp_traits::{AuthorNotingHook, DistributeRewards, GetCurrentContainerChains}, +}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + pub type BalanceOf = + <::Currency as Inspect<::AccountId>>::Balance; + pub type CreditOf = Credit<::AccountId, ::Currency>; + + /// Inflation rewards pallet. + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_: BlockNumberFor) -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // Collect indistributed rewards, if any + // Any parachain we have not rewarded is handled by onUnbalanced + let not_distributed_rewards = + if let Some(chains_to_reward) = ChainsToReward::::take() { + // Collect and sum all undistributed rewards + let rewards_not_distributed: BalanceOf = chains_to_reward + .rewards_per_chain + .saturating_mul((chains_to_reward.para_ids.len() as u32).into()); + T::Currency::withdraw( + &T::PendingRewardsAccount::get(), + rewards_not_distributed, + Precision::BestEffort, + Preservation::Expendable, + Fortitude::Force, + ) + .unwrap_or(CreditOf::::zero()) + } else { + CreditOf::::zero() + }; + + // Get the number of chains at this block (tanssi + container chain blocks) + weight += T::DbWeight::get().reads_writes(1, 1); + let registered_para_ids = T::ContainerChains::current_container_chains(); + let number_of_chains: BalanceOf = + ((registered_para_ids.len() as u32).saturating_add(1)).into(); + + // Issue new supply + let new_supply = + T::Currency::issue(T::InflationRate::get() * T::Currency::total_issuance()); + + // Split staking reward portion + let total_rewards = T::RewardsPortion::get() * new_supply.peek(); + let (rewards_credit, reminder_credit) = new_supply.split(total_rewards); + + let rewards_per_chain: BalanceOf = rewards_credit.peek() / number_of_chains; + let (mut total_reminder, staking_rewards) = rewards_credit.split_merge( + total_rewards % number_of_chains, + (reminder_credit, CreditOf::::zero()), + ); + + // Deposit the new supply dedicated to rewards in the pending rewards account + if let Err(undistributed_rewards) = + T::Currency::resolve(&T::PendingRewardsAccount::get(), staking_rewards) + { + total_reminder = total_reminder.merge(undistributed_rewards); + } + + // Keep track of chains to reward + ChainsToReward::::put(ChainsToRewardValue { + para_ids: registered_para_ids, + rewards_per_chain, + }); + + // Let the runtime handle the non-staking part + T::OnUnbalanced::on_unbalanced(not_distributed_rewards.merge(total_reminder)); + + weight += Self::reward_orchestrator_author(); + + weight + } + } + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + type Currency: Inspect + Balanced; + + type ContainerChains: GetCurrentContainerChains; + + /// Get block author for self chain + type GetSelfChainBlockAuthor: Get; + + /// Inflation rate per orchestrator block (proportion of the total issuance) + #[pallet::constant] + type InflationRate: Get; + + /// What to do with the new supply not dedicated to staking + type OnUnbalanced: OnUnbalanced>; + + /// The account that will store rewards waiting to be paid out + #[pallet::constant] + type PendingRewardsAccount: Get; + + /// Staking rewards distribution implementation + type StakingRewardsDistributor: DistributeRewards>; + + /// Proportion of the new supply dedicated to staking + #[pallet::constant] + type RewardsPortion: Get; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Rewarding orchestrator author + RewardedOrchestrator { + account_id: T::AccountId, + balance: BalanceOf, + }, + /// Rewarding container author + RewardedContainer { + account_id: T::AccountId, + para_id: ParaId, + balance: BalanceOf, + }, + } + + /// Container chains to reward per block + #[pallet::storage] + #[pallet::getter(fn container_chains_to_reward)] + pub(super) type ChainsToReward = + StorageValue<_, ChainsToRewardValue, OptionQuery>; + #[derive(Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo)] + #[scale_info(skip_type_params(T))] + pub struct ChainsToRewardValue { + pub para_ids: BoundedVec< + ParaId, + ::MaxContainerChains, + >, + pub rewards_per_chain: BalanceOf, + } + + impl Pallet { + fn reward_orchestrator_author() -> Weight { + let mut total_weight = T::DbWeight::get().reads(1); + let orchestrator_author = T::GetSelfChainBlockAuthor::get(); + + if let Some(chains_to_reward) = ChainsToReward::::get() { + total_weight += T::DbWeight::get().reads(1); + match T::StakingRewardsDistributor::distribute_rewards( + orchestrator_author.clone(), + T::Currency::withdraw( + &T::PendingRewardsAccount::get(), + chains_to_reward.rewards_per_chain, + Precision::BestEffort, + Preservation::Expendable, + Fortitude::Force, + ) + .unwrap_or(CreditOf::::zero()), + ) { + Ok(frame_support::dispatch::PostDispatchInfo { actual_weight, .. }) => { + Self::deposit_event(Event::RewardedOrchestrator { + account_id: orchestrator_author, + balance: chains_to_reward.rewards_per_chain, + }); + + if let Some(weight) = actual_weight { + total_weight += weight + } + } + Err(e) => { + log::debug!("Fail to distribute rewards: {:?}", e) + } + } + } else { + panic!("ChainsToReward not filled"); + } + + total_weight + } + } +} + +// This function should only be used to **reward** a container author. +// There will be no additional check other than checking if we have already +// rewarded this author for **in this tanssi block** +// Any additional check should be done in the calling function +// TODO: consider passing a vector here +impl AuthorNotingHook for Pallet { + fn on_container_author_noted( + author: &T::AccountId, + _block_number: BlockNumber, + para_id: ParaId, + ) -> Weight { + let mut total_weight = T::DbWeight::get().reads_writes(1, 0); + // We take chains to reward, to see what containers are left to reward + if let Some(mut container_chains_to_reward) = ChainsToReward::::get() { + // If we find the index is because we still have not rewarded it + if let Ok(index) = container_chains_to_reward.para_ids.binary_search(¶_id) { + // we distribute rewards to the author + match T::StakingRewardsDistributor::distribute_rewards( + author.clone(), + T::Currency::withdraw( + &T::PendingRewardsAccount::get(), + container_chains_to_reward.rewards_per_chain, + Precision::BestEffort, + Preservation::Expendable, + Fortitude::Force, + ) + .unwrap_or(CreditOf::::zero()), + ) { + Ok(frame_support::dispatch::PostDispatchInfo { actual_weight, .. }) => { + Self::deposit_event(Event::RewardedContainer { + account_id: author.clone(), + balance: container_chains_to_reward.rewards_per_chain, + para_id, + }); + if let Some(weight) = actual_weight { + total_weight += weight + } + } + Err(e) => { + log::debug!("Fail to distribute rewards: {:?}", e) + } + } + // we remove the para id from container-chains to reward + // this makes sure we dont reward it twice in the same block + container_chains_to_reward.para_ids.remove(index); + + total_weight += T::DbWeight::get().writes(1); + // Keep track of chains to reward + ChainsToReward::::put(container_chains_to_reward); + } + } + total_weight + } +} diff --git a/pallets/inflation-rewards/src/mock.rs b/pallets/inflation-rewards/src/mock.rs new file mode 100644 index 0000000..2cf1d34 --- /dev/null +++ b/pallets/inflation-rewards/src/mock.rs @@ -0,0 +1,229 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{self as pallet_inflation_rewards}, + bounded_collections::bounded_vec, + dp_core::ParaId, + frame_support::{ + pallet_prelude::*, + parameter_types, + traits::{ + fungible::{Balanced, Credit}, + ConstU64, Everything, + }, + }, + sp_core::H256, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, Perbill, + }, +}; + +type Block = frame_system::mocking::MockBlock; +pub type AccountId = u64; +pub type Balance = u128; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + InflationRewards: pallet_inflation_rewards, + MockData: mock_data, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type Block = Block; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; + type RuntimeTask = (); +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} + +impl pallet_balances::Config for Test { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); + type MaxHolds = ConstU32<5>; + type WeightInfo = (); +} + +// Pallet to provide some mock data, used to test +#[frame_support::pallet] +pub mod mock_data { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn mock)] + pub(super) type Mock = StorageValue<_, Mocks, ValueQuery>; + + impl Pallet { + pub fn get() -> Mocks { + Mock::::get() + } + pub fn mutate(f: F) -> R + where + F: FnOnce(&mut Mocks) -> R, + { + Mock::::mutate(f) + } + } +} + +impl mock_data::Config for Test {} + +#[derive(Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Mocks { + pub container_chains: BoundedVec>, + pub orchestrator_author: AccountId, +} + +impl Default for Mocks { + fn default() -> Self { + Self { + container_chains: bounded_vec![1001.into()], + orchestrator_author: 1, + } + } +} + +pub struct MockContainerChainGetter; + +impl tp_traits::GetCurrentContainerChains for MockContainerChainGetter { + type MaxContainerChains = ConstU32<5>; + + fn current_container_chains() -> BoundedVec { + MockData::mock().container_chains + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_current_container_chains(container_chains: &[ParaId]) { + MockData::mutate(|m| { + m.container_chains = container_chains.to_vec().try_into().unwrap(); + }); + } +} + +pub struct MockGetSelfChainBlockAuthor; + +impl Get for MockGetSelfChainBlockAuthor { + fn get() -> AccountId { + MockData::mock().orchestrator_author + } +} + +pub struct OnUnbalancedInflation; +impl frame_support::traits::OnUnbalanced> for OnUnbalancedInflation { + fn on_nonzero_unbalanced(credit: Credit) { + let _ = >::resolve(&OnUnbalancedInflationAccount::get(), credit); + } +} + +pub struct MockRewardsDistributor; +impl tp_traits::DistributeRewards> + for MockRewardsDistributor +{ + fn distribute_rewards( + rewarded: AccountId, + amount: Credit, + ) -> DispatchResultWithPostInfo { + <::Currency as Balanced>::resolve( + &rewarded, amount, + ) + .map_err(|_| DispatchError::NoProviders)?; + Ok(().into()) + } +} + +parameter_types! { + pub OnUnbalancedInflationAccount: AccountId = 0; + pub PendingRewardsAccount: AccountId = 99; + pub const RewardsPortion: Perbill = Perbill::from_percent(70); + pub const InflationRate: Perbill = Perbill::from_percent(1); +} + +impl pallet_inflation_rewards::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ContainerChains = MockContainerChainGetter; + type GetSelfChainBlockAuthor = MockGetSelfChainBlockAuthor; + type InflationRate = InflationRate; + type OnUnbalanced = OnUnbalancedInflation; + type PendingRewardsAccount = PendingRewardsAccount; + type StakingRewardsDistributor = MockRewardsDistributor; + type RewardsPortion = RewardsPortion; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + let balances = vec![(0, 10_000)]; + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() +} diff --git a/pallets/inflation-rewards/src/tests.rs b/pallets/inflation-rewards/src/tests.rs new file mode 100644 index 0000000..f23f0de --- /dev/null +++ b/pallets/inflation-rewards/src/tests.rs @@ -0,0 +1,249 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{mock::*, Config, *}, + frame_support::{pallet_prelude::*, traits::fungible::Inspect}, + sp_runtime::Permill, +}; + +fn get_balance(who: &AccountId) -> Balance { + <::Currency as Inspect>::balance(who) +} + +fn get_total_issuance() -> Balance { + <::Currency as Inspect>::total_issuance() +} + +#[test] +fn test_increase_supply() { + new_test_ext().execute_with(|| { + let total_supply_0 = get_total_issuance(); + + as Hooks>::on_initialize(1); + let total_supply_1 = get_total_issuance(); + assert_eq!( + total_supply_1, + total_supply_0 + (::InflationRate::get() * total_supply_0), + ); + + as Hooks>::on_initialize(2); + let total_supply_2 = get_total_issuance(); + assert_eq!( + total_supply_2, + total_supply_1 + (::InflationRate::get() * total_supply_1), + ); + }); +} + +#[test] +fn test_undistributed_rewards() { + new_test_ext().execute_with(|| { + let total_supply_0 = get_total_issuance(); + let initial_balance = get_balance(&OnUnbalancedInflationAccount::get()); + + as Hooks>::on_initialize(1); + let total_supply_1 = get_total_issuance(); + + let new_supply = total_supply_1 - total_supply_0; + + // The OnUnbalancedInflationAccount should receive 30% of the new supply + assert_eq!( + get_balance(&OnUnbalancedInflationAccount::get()), + initial_balance + (Permill::from_percent(30) * new_supply), + ); + }); +} + +#[test] +fn test_reward_orchestrator_author() { + new_test_ext().execute_with(|| { + let author = ::GetSelfChainBlockAuthor::get(); + let author_balance = get_balance(&author); + + let total_supply_0 = get_total_issuance(); + as Hooks>::on_initialize(1); + let total_supply_1 = get_total_issuance(); + + let new_supply = total_supply_1 - total_supply_0; + + assert_eq!( + get_balance(&author), + // 70% rewards for 2 chains, so 35% per chain + author_balance + (Permill::from_percent(35) * new_supply), + ); + }); +} + +#[test] +fn test_reward_orchestrator_author_less_if_more_chains() { + new_test_ext().execute_with(|| { + // Add 2 container chains + MockData::mutate(|data| { + data.container_chains.try_push(1002.into()).unwrap(); + data.container_chains.try_push(1003.into()).unwrap(); + }); + + let author = ::GetSelfChainBlockAuthor::get(); + let author_balance = get_balance(&author); + + let total_supply_0 = get_total_issuance(); + as Hooks>::on_initialize(1); + let total_supply_1 = get_total_issuance(); + + let new_supply = total_supply_1 - total_supply_0; + + assert_eq!( + get_balance(&author), + // 70% rewards for 3 chains, so 17.5% per chain + author_balance + (Permill::from_perthousand(175) * new_supply), + ); + }); +} + +#[test] +fn test_reward_container_chain_author() { + new_test_ext().execute_with(|| { + let container_author = 2; + let container_author_2 = 3; + let container_author_balance = get_balance(&container_author); + let container_author_balance_2 = get_balance(&container_author_2); + + let total_supply_0 = get_total_issuance(); + as Hooks>::on_initialize(1); + let total_supply_1 = get_total_issuance(); + + let new_supply_1 = total_supply_1 - total_supply_0; + + // Note container author + let registered_para_ids = ::ContainerChains::current_container_chains(); + as AuthorNotingHook>::on_container_author_noted( + &container_author, + 1, + registered_para_ids[0], + ); + + // Author should be rewarded immediately + assert_eq!( + get_balance(&container_author), + // 70% rewards for 2 chains, so 35% per chain + container_author_balance + (Permill::from_percent(35) * new_supply_1), + ); + + as Hooks>::on_initialize(2); + let total_supply_2 = get_total_issuance(); + let new_supply_2 = total_supply_2 - total_supply_1; + + // Note next container author + as AuthorNotingHook>::on_container_author_noted( + &container_author_2, + 2, + registered_para_ids[0], + ); + + // Author should be rewarded immediately + assert_eq!( + get_balance(&container_author_2), + // 70% rewards for 2 chains, so 35% per chain + container_author_balance_2 + (Permill::from_percent(35) * new_supply_2), + ); + }); +} + +#[test] +fn test_cannot_reward_twice_in_same_tanssi_block() { + new_test_ext().execute_with(|| { + let container_author = 2; + let container_author_balance = get_balance(&container_author); + + let total_supply_0 = get_total_issuance(); + as Hooks>::on_initialize(1); + let total_supply_1 = get_total_issuance(); + + let new_supply_1 = total_supply_1 - total_supply_0; + + // Note container author + let registered_para_ids = ::ContainerChains::current_container_chains(); + as AuthorNotingHook>::on_container_author_noted( + &container_author, + 1, + registered_para_ids[0], + ); + + // Regardless if we inject a new block, we cannot reward twice the same paraId + as AuthorNotingHook>::on_container_author_noted( + &container_author, + 2, + registered_para_ids[0], + ); + + // Author should be rewarded only once + assert_eq!( + get_balance(&container_author), + // 70% rewards for 2 chains, so 35% per chain + container_author_balance + (Permill::from_percent(35) * new_supply_1), + ); + }); +} + +#[test] +fn test_non_claimed_rewards_go_to_on_unbalanced() { + new_test_ext().execute_with(|| { + let container_author = 2; + let container_author_balance = get_balance(&container_author); + + as Hooks>::on_initialize(1); + let on_unbalanced_account = get_balance(&OnUnbalancedInflationAccount::get()); + + let total_supply_1 = get_total_issuance(); + + // We initilize the next block without claiming rewards for the container + // author should have not been rewarded and the onUNbalanced hook should kick in + // we use block 2 because it has reminder + as Hooks>::on_initialize(2); + + let total_supply_2 = get_total_issuance(); + + let new_supply_2 = total_supply_2 - total_supply_1; + + // OnUnbalancedInflationAccount::get() should be rewarded with the non-claimed + // rewards + // The onUnbalanedInflationAccount should have: + // the non-reward portion ((Permill::from_percent(30) * new_supply_1)) + // the reminder ((Permill::from_percent(70) * suppl7 % number of container chains)) + // the non-claimed rewards + let staking_rewards = Permill::from_percent(70) * new_supply_2; + let non_staking_rewards = new_supply_2 - staking_rewards; + // (orchestrator plus container); + let reminder = staking_rewards % 2; + + assert_eq!( + get_balance(&OnUnbalancedInflationAccount::get()), + // 70% rewards for 2 chains, so 35% per chain + on_unbalanced_account + + non_staking_rewards + + reminder + + (Permill::from_percent(35) * new_supply_2), + ); + + // and the author is not rewarded + assert_eq!( + get_balance(&container_author), + // 70% rewards for 2 chains, so 35% per chain + container_author_balance, + ); + }); +} diff --git a/pallets/initializer/Cargo.toml b/pallets/initializer/Cargo.toml new file mode 100644 index 0000000..c2bf206 --- /dev/null +++ b/pallets/initializer/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "pallet-initializer" +authors = { workspace = true } +description = "Initializer pallet that allows to orchestrate what happens on session changes" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-session = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "frame-support/std", + "frame-system/std", + "pallet-session/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-session/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/initializer/src/lib.rs b/pallets/initializer/src/lib.rs new file mode 100644 index 0000000..60f622f --- /dev/null +++ b/pallets/initializer/src/lib.rs @@ -0,0 +1,141 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Initializer Pallet +//! +//! This pallet is in charge of organizing what happens on session changes. +//! In particular this pallet has implemented the OneSessionHandler trait +//! which will be called upon a session change. There it will call the +//! SessionHandler config trait + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +pub use pallet::*; +use { + frame_support::{pallet_prelude::*, traits::OneSessionHandler}, + scale_info::TypeInfo, + sp_runtime::{traits::AtLeast32BitUnsigned, RuntimeAppPublic}, + sp_std::prelude::*, +}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + // The apply_new_session trait. We need to comply with this + pub trait ApplyNewSession { + fn apply_new_session( + changed: bool, + session_index: T::SessionIndex, + all_validators: Vec<(T::AccountId, T::AuthorityId)>, + queued: Vec<(T::AccountId, T::AuthorityId)>, + ); + } + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned; + + /// The identifier type for an authority. + type AuthorityId: Member + + Parameter + + RuntimeAppPublic + + MaybeSerializeDeserialize + + MaxEncodedLen; + + type SessionHandler: ApplyNewSession; + } +} + +impl Pallet { + /// Should be called when a new session occurs. If `queued` is `None`, + /// the `validators` are considered queued. + fn on_new_session<'a, I>( + changed: bool, + session_index: T::SessionIndex, + validators: I, + queued: Option, + ) where + I: Iterator + 'a, + { + let validators: Vec<_> = validators.map(|(k, v)| (k.clone(), v)).collect(); + let queued: Vec<_> = if let Some(queued) = queued { + queued.map(|(k, v)| (k.clone(), v)).collect() + } else { + validators.clone() + }; + + T::SessionHandler::apply_new_session(changed, session_index, validators, queued); + } + + /// Should be called when a new session occurs. Buffers the session notification to be applied + /// at the end of the block. If `queued` is `None`, the `validators` are considered queued. + fn on_genesis_session<'a, I>(validators: I) + where + I: Iterator + 'a, + { + >::on_new_session(false, 0u32.into(), validators, None); + } + + // Allow to trigger `on_new_session` in tests, this is needed as long as `pallet_session` is not + // implemented in mock. + #[cfg(any(test, feature = "runtime-benchmarks"))] + pub(crate) fn test_trigger_on_new_session<'a, I>( + changed: bool, + session_index: T::SessionIndex, + validators: I, + queued: Option, + ) where + I: Iterator + 'a, + { + Self::on_new_session(changed, session_index, validators, queued) + } +} + +impl sp_runtime::BoundToRuntimeAppPublic for Pallet { + type Public = T::AuthorityId; +} + +impl OneSessionHandler for Pallet { + type Key = T::AuthorityId; + + fn on_genesis_session<'a, I>(validators: I) + where + I: Iterator + 'a, + { + >::on_genesis_session(validators); + } + + fn on_new_session<'a, I>(changed: bool, validators: I, queued: I) + where + I: Iterator + 'a, + { + let session_index = >::current_index(); + >::on_new_session(changed, session_index.into(), validators, Some(queued)); + } + + fn on_disabled(_i: u32) {} +} diff --git a/pallets/initializer/src/mock.rs b/pallets/initializer/src/mock.rs new file mode 100644 index 0000000..3e97c9a --- /dev/null +++ b/pallets/initializer/src/mock.rs @@ -0,0 +1,107 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate as pallet_initializer, + frame_support::traits::{ConstU16, ConstU64}, + frame_system as system, + sp_core::H256, + sp_runtime::{ + testing::UintAuthorityId, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, + }, + sp_std::cell::RefCell, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Initializer: pallet_initializer, + } +); + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = (); +} + +thread_local! { + pub static SESSION_CHANGE_VALIDATORS: RefCell)>> = const { RefCell::new(None) }; +} + +pub fn session_change_validators() -> Option<(u32, Vec)> { + SESSION_CHANGE_VALIDATORS.with(|q| (*q.borrow()).clone()) +} + +pub struct OwnApplySession; +impl pallet_initializer::ApplyNewSession for OwnApplySession { + fn apply_new_session( + _changed: bool, + session_index: u32, + all_validators: Vec<(u64, UintAuthorityId)>, + _queued: Vec<(u64, UintAuthorityId)>, + ) { + let validators: Vec<_> = all_validators.iter().map(|(k, _)| *k).collect(); + SESSION_CHANGE_VALIDATORS.with(|r| *r.borrow_mut() = Some((session_index, validators))); + } +} + +impl pallet_initializer::Config for Test { + type SessionIndex = u32; + + /// The identifier type for an authority. + type AuthorityId = UintAuthorityId; + + type SessionHandler = OwnApplySession; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + // Start with None in the global var + SESSION_CHANGE_VALIDATORS.with(|r| *r.borrow_mut() = None); + + system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} diff --git a/pallets/initializer/src/tests.rs b/pallets/initializer/src/tests.rs new file mode 100644 index 0000000..9cde050 --- /dev/null +++ b/pallets/initializer/src/tests.rs @@ -0,0 +1,49 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + super::*, + crate::mock::{new_test_ext, session_change_validators, Initializer}, +}; + +#[test] +fn session_0_is_instantly_applied() { + new_test_ext().execute_with(|| { + Initializer::test_trigger_on_new_session( + false, + 0, + Vec::new().into_iter(), + Some(Vec::new().into_iter()), + ); + + assert_eq!(session_change_validators(), Some((0, Vec::new()))); + }); +} + +#[test] +fn session_change_applied() { + new_test_ext().execute_with(|| { + Initializer::test_trigger_on_new_session( + false, + 1, + Vec::new().into_iter(), + Some(Vec::new().into_iter()), + ); + + // Session change validators are applied + assert_eq!(session_change_validators(), Some((1, Vec::new()))); + }); +} diff --git a/pallets/invulnerables/Cargo.toml b/pallets/invulnerables/Cargo.toml new file mode 100644 index 0000000..c36ae6e --- /dev/null +++ b/pallets/invulnerables/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "pallet-invulnerables" +authors = { workspace = true } +description = "Simple pallet to store invulnarable collators." +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +log = { workspace = true } +parity-scale-codec = { workspace = true } +rand = { workspace = true, optional = true } +scale-info = { workspace = true, features = [ "derive" ] } + +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-runtime = { workspace = true } +sp-staking = { workspace = true } +sp-std = { workspace = true } +tp-traits = { workspace = true } + +frame-benchmarking = { workspace = true } + +pallet-balances = { workspace = true, optional = true } +pallet-session = { workspace = true } + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances/std", + "pallet-session/std", + "parity-scale-codec/std", + "rand?/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-staking/std", + "sp-std/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "rand", + "sp-runtime/runtime-benchmarks", + "sp-staking/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] + +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances?/try-runtime", + "pallet-session/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/invulnerables/src/benchmarking.rs b/pallets/invulnerables/src/benchmarking.rs new file mode 100644 index 0000000..a80683a --- /dev/null +++ b/pallets/invulnerables/src/benchmarking.rs @@ -0,0 +1,245 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Benchmarking setup for pallet-invulnerables + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +#[allow(unused)] +use crate::Pallet as InvulnerablesPallet; +use { + frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}, + frame_support::{ + pallet_prelude::*, + traits::{tokens::fungible::Balanced, Currency, EnsureOrigin, Get}, + }, + frame_system::{EventRecord, RawOrigin}, + pallet_session::{self as session, SessionManager}, + sp_runtime::traits::AtLeast32BitUnsigned, + sp_std::prelude::*, + tp_traits::DistributeRewards, +}; +const SEED: u32 = 0; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +fn create_funded_user( + string: &'static str, + n: u32, + balance_factor: u32, +) -> T::AccountId { + let user = account(string, n, SEED); + let balance = as Currency>::minimum_balance() + * balance_factor.into(); + let _ = as Currency>::make_free_balance_be( + &user, balance, + ); + user +} + +fn keys(c: u32) -> ::Keys { + use rand::{RngCore, SeedableRng}; + + let keys = { + let mut keys = [0u8; 128]; + + if c > 0 { + let mut rng = rand::rngs::StdRng::seed_from_u64(c as u64); + rng.fill_bytes(&mut keys); + } + + keys + }; + + Decode::decode(&mut &keys[..]).unwrap() +} + +fn invulnerable( + c: u32, +) -> (T::AccountId, T::CollatorId, ::Keys) { + let funded_user = create_funded_user::("candidate", c, 100); + let collator_id = T::CollatorIdOf::convert(funded_user.clone()) + .expect("Converstion of account id of collator id failed."); + (funded_user, collator_id, keys::(c)) +} + +fn invulnerables< + T: Config + frame_system::Config + pallet_session::Config + pallet_balances::Config, +>( + count: u32, +) -> Vec<(T::AccountId, T::CollatorId)> { + let invulnerables = (0..count).map(|c| invulnerable::(c)).collect::>(); + + for (who, _collator_id, keys) in invulnerables.clone() { + >::set_keys(RawOrigin::Signed(who).into(), keys, Vec::new()).unwrap(); + } + + invulnerables + .into_iter() + .map(|(who, collator_id, _)| (who, collator_id)) + .collect() +} + +pub type BalanceOf = + <::Currency as frame_support::traits::fungible::Inspect< + ::AccountId, + >>::Balance; + +pub(crate) fn currency_issue( + amount: BalanceOf, +) -> crate::CreditOf { + <::Currency as Balanced>::issue(amount) +} + +#[allow(clippy::multiple_bound_locations)] +#[benchmarks(where T: session::Config + pallet_balances::Config, BalanceOf: AtLeast32BitUnsigned)] +mod benchmarks { + use super::*; + + #[benchmark] + fn add_invulnerable( + b: Linear<1, { T::MaxInvulnerables::get() - 1 }>, + ) -> Result<(), BenchmarkError> { + let origin = + T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + // now we need to fill up invulnerables + let mut invulnerables = invulnerables::(b); + invulnerables.sort(); + + let (_account_ids, collator_ids): (Vec, Vec) = + invulnerables.into_iter().unzip(); + + let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> = + frame_support::BoundedVec::try_from(collator_ids).unwrap(); + >::put(invulnerables); + + let (new_invulnerable, _collator_id, keys) = invulnerable::(b + 1); + >::set_keys( + RawOrigin::Signed(new_invulnerable.clone()).into(), + keys, + Vec::new(), + ) + .unwrap(); + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, new_invulnerable.clone()); + + assert_last_event::( + Event::InvulnerableAdded { + account_id: new_invulnerable, + } + .into(), + ); + Ok(()) + } + + #[benchmark] + fn remove_invulnerable( + b: Linear<{ 1 }, { T::MaxInvulnerables::get() }>, + ) -> Result<(), BenchmarkError> { + let origin = + T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let mut invulnerables = invulnerables::(b); + invulnerables.sort(); + + let (account_ids, collator_ids): (Vec, Vec) = + invulnerables.into_iter().unzip(); + + let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> = + frame_support::BoundedVec::try_from(collator_ids).unwrap(); + >::put(invulnerables); + + let to_remove = account_ids.last().unwrap().clone(); + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, to_remove.clone()); + + assert_last_event::( + Event::InvulnerableRemoved { + account_id: to_remove, + } + .into(), + ); + Ok(()) + } + + // worst case for new session. + #[benchmark] + fn new_session(r: Linear<1, { T::MaxInvulnerables::get() }>) -> Result<(), BenchmarkError> { + let origin = + T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + frame_system::Pallet::::set_block_number(0u32.into()); + // now we need to fill up invulnerables + let mut invulnerables = invulnerables::(r); + invulnerables.sort(); + + let (account_ids, _collator_ids): (Vec, Vec) = + invulnerables.into_iter().unzip(); + + for account in account_ids { + >::add_invulnerable(origin.clone(), account) + .expect("add invulnerable failed"); + } + + #[block] + { + as SessionManager<_>>::new_session(0); + } + + Ok(()) + } + + #[benchmark] + fn reward_invulnerable( + b: Linear<{ 1 }, { T::MaxInvulnerables::get() }>, + ) -> Result<(), BenchmarkError> where { + let mut invulnerables = invulnerables::(b); + invulnerables.sort(); + + let (account_ids, collator_ids): (Vec, Vec) = + invulnerables.into_iter().unzip(); + + let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> = + frame_support::BoundedVec::try_from(collator_ids).unwrap(); + >::put(invulnerables); + let to_reward = account_ids.first().unwrap().clone(); + // Create new supply for rewards + let new_supply = currency_issue::(1000u32.into()); + #[block] + { + let _ = InvulnerableRewardDistribution::::distribute_rewards( + to_reward, new_supply, + ); + } + + Ok(()) + } + impl_benchmark_test_suite!( + InvulnerablesPallet, + crate::mock::new_test_ext(), + crate::mock::Test, + ); +} diff --git a/pallets/invulnerables/src/lib.rs b/pallets/invulnerables/src/lib.rs new file mode 100644 index 0000000..2c16885 --- /dev/null +++ b/pallets/invulnerables/src/lib.rs @@ -0,0 +1,307 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Invulnerables pallet. +//! +//! A pallet to manage invulnerable collators in a parachain. +//! +//! ## Terminology +//! +//! - Collator: A parachain block producer. +//! - Invulnerable: An account appointed by governance and guaranteed to be in the collator set. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +use { + core::marker::PhantomData, + sp_runtime::{traits::Convert, TokenError}, +}; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; + +#[frame_support::pallet] +pub mod pallet { + pub use crate::weights::WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + use frame_support::traits::Currency; + + use { + frame_support::{ + dispatch::DispatchResultWithPostInfo, + pallet_prelude::*, + traits::{EnsureOrigin, ValidatorRegistration}, + BoundedVec, DefaultNoBound, + }, + frame_system::pallet_prelude::*, + pallet_session::SessionManager, + sp_runtime::traits::Convert, + sp_staking::SessionIndex, + sp_std::vec::Vec, + }; + + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + /// A convertor from collators id. Since this pallet does not have stash/controller, this is + /// just identity. + pub struct IdentityCollator; + impl sp_runtime::traits::Convert> for IdentityCollator { + fn convert(t: T) -> Option { + Some(t) + } + } + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Origin that can dictate updating parameters of this pallet. + type UpdateOrigin: EnsureOrigin; + + /// Maximum number of invulnerables. + #[pallet::constant] + type MaxInvulnerables: Get; + + /// A stable ID for a collator. + type CollatorId: Member + Parameter + MaybeSerializeDeserialize + MaxEncodedLen + Ord; + + /// A conversion from account ID to collator ID. + /// + /// Its cost must be at most one storage read. + type CollatorIdOf: Convert>; + + /// Validate a user is registered + type CollatorRegistration: ValidatorRegistration; + + /// The weight information of this pallet. + type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type Currency: Currency + + frame_support::traits::fungible::Balanced; + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + /// The invulnerable, permissioned collators. This list must be sorted. + #[pallet::storage] + #[pallet::getter(fn invulnerables)] + pub type Invulnerables = + StorageValue<_, BoundedVec, ValueQuery>; + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig { + pub invulnerables: Vec, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + let duplicate_invulnerables = self + .invulnerables + .iter() + .collect::>(); + assert!( + duplicate_invulnerables.len() == self.invulnerables.len(), + "duplicate invulnerables in genesis." + ); + + let bounded_invulnerables = + BoundedVec::<_, T::MaxInvulnerables>::try_from(self.invulnerables.clone()) + .expect("genesis invulnerables are more than T::MaxInvulnerables"); + + >::put(bounded_invulnerables); + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// New Invulnerables were set. + NewInvulnerables { invulnerables: Vec }, + /// A new Invulnerable was added. + InvulnerableAdded { account_id: T::AccountId }, + /// An Invulnerable was removed. + InvulnerableRemoved { account_id: T::AccountId }, + /// An account was unable to be added to the Invulnerables because they did not have keys + /// registered. Other Invulnerables may have been set. + InvalidInvulnerableSkipped { account_id: T::AccountId }, + } + + #[pallet::error] + pub enum Error { + /// There are too many Invulnerables. + TooManyInvulnerables, + /// Account is already an Invulnerable. + AlreadyInvulnerable, + /// Account is not an Invulnerable. + NotInvulnerable, + /// Account does not have keys registered + NoKeysRegistered, + /// Unable to derive collator id from account id + UnableToDeriveCollatorId, + } + + #[pallet::call] + impl Pallet { + /// Add a new account `who` to the list of `Invulnerables` collators. + /// + /// The origin for this call must be the `UpdateOrigin`. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::add_invulnerable( + T::MaxInvulnerables::get().saturating_sub(1), + ))] + pub fn add_invulnerable( + origin: OriginFor, + who: T::AccountId, + ) -> DispatchResultWithPostInfo { + T::UpdateOrigin::ensure_origin(origin)?; + // don't let one unprepared collator ruin things for everyone. + let maybe_collator_id = T::CollatorIdOf::convert(who.clone()) + .filter(T::CollatorRegistration::is_registered); + + let collator_id = maybe_collator_id.ok_or(Error::::NoKeysRegistered)?; + + >::try_mutate(|invulnerables| -> DispatchResult { + if invulnerables.contains(&collator_id) { + Err(Error::::AlreadyInvulnerable)?; + } + invulnerables + .try_push(collator_id.clone()) + .map_err(|_| Error::::TooManyInvulnerables)?; + Ok(()) + })?; + + Self::deposit_event(Event::InvulnerableAdded { account_id: who }); + + let weight_used = T::WeightInfo::add_invulnerable( + Invulnerables::::decode_len() + .unwrap_or_default() + .try_into() + .unwrap_or(T::MaxInvulnerables::get().saturating_sub(1)), + ); + + Ok(Some(weight_used).into()) + } + + /// Remove an account `who` from the list of `Invulnerables` collators. `Invulnerables` must + /// be sorted. + /// + /// The origin for this call must be the `UpdateOrigin`. + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::remove_invulnerable(T::MaxInvulnerables::get()))] + pub fn remove_invulnerable(origin: OriginFor, who: T::AccountId) -> DispatchResult { + T::UpdateOrigin::ensure_origin(origin)?; + + let collator_id = T::CollatorIdOf::convert(who.clone()) + .ok_or(Error::::UnableToDeriveCollatorId)?; + + >::try_mutate(|invulnerables| -> DispatchResult { + let pos = invulnerables + .iter() + .position(|x| x == &collator_id) + .ok_or(Error::::NotInvulnerable)?; + invulnerables.remove(pos); + Ok(()) + })?; + + Self::deposit_event(Event::InvulnerableRemoved { account_id: who }); + Ok(()) + } + } + + /// Play the role of the session manager. + impl SessionManager for Pallet { + fn new_session(index: SessionIndex) -> Option> { + log::info!( + "assembling new invulnerable collators for new session {} at #{:?}", + index, + >::block_number(), + ); + + let invulnerables = Self::invulnerables().to_vec(); + frame_system::Pallet::::register_extra_weight_unchecked( + T::WeightInfo::new_session(invulnerables.len() as u32), + DispatchClass::Mandatory, + ); + Some(invulnerables) + } + fn start_session(_: SessionIndex) { + // we don't care. + } + fn end_session(_: SessionIndex) { + // we don't care. + } + } +} + +/// If the rewarded account is an Invulnerable, distribute the entire reward +/// amount to them. Otherwise use the `Fallback` distribution. +pub struct InvulnerableRewardDistribution( + PhantomData<(Runtime, Currency, Fallback)>, +); + +use {frame_support::pallet_prelude::Weight, sp_runtime::traits::Get}; + +type CreditOf = + frame_support::traits::fungible::Credit<::AccountId, Currency>; +pub type AccountIdOf = ::AccountId; + +impl + tp_traits::DistributeRewards, CreditOf> + for InvulnerableRewardDistribution +where + Runtime: frame_system::Config + Config, + Fallback: tp_traits::DistributeRewards, CreditOf>, + Currency: frame_support::traits::fungible::Balanced>, +{ + fn distribute_rewards( + rewarded: AccountIdOf, + amount: CreditOf, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + let mut total_weight = Weight::zero(); + let collator_id = Runtime::CollatorIdOf::convert(rewarded.clone()) + .ok_or(Error::::UnableToDeriveCollatorId)?; + // weight to read invulnerables + total_weight += Runtime::DbWeight::get().reads(1); + if !Invulnerables::::get().contains(&collator_id) { + let post_info = Fallback::distribute_rewards(rewarded, amount)?; + if let Some(weight) = post_info.actual_weight { + total_weight += weight; + } + } else { + Currency::resolve(&rewarded, amount).map_err(|_| TokenError::NotExpendable)?; + total_weight += + Runtime::WeightInfo::reward_invulnerable(Runtime::MaxInvulnerables::get()) + } + Ok(Some(total_weight).into()) + } +} diff --git a/pallets/invulnerables/src/mock.rs b/pallets/invulnerables/src/mock.rs new file mode 100644 index 0000000..62a843b --- /dev/null +++ b/pallets/invulnerables/src/mock.rs @@ -0,0 +1,209 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. +// Tanssi 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 Tanssi. If not, see + +use { + super::*, + crate as invulnerables, + frame_support::{ + ord_parameter_types, parameter_types, + traits::{ConstU32, ValidatorRegistration}, + }, + frame_system::{self as system, EnsureSignedBy}, + pallet_balances::AccountData, + sp_core::H256, + sp_runtime::{ + testing::UintAuthorityId, + traits::{BlakeTwo256, IdentityLookup, OpaqueKeys}, + BuildStorage, RuntimeAppPublic, + }, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Invulnerables: invulnerables, + Session: pallet_session, + Balances: pallet_balances, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type Nonce = u64; + type Block = Block; + type RuntimeTask = (); +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 5; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Balance = u64; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); + type FreezeIdentifier = (); + type MaxLocks = (); + type MaxReserves = MaxReserves; + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +ord_parameter_types! { + pub const RootAccount: u64 = 777; +} + +pub struct IsRegistered; +impl ValidatorRegistration for IsRegistered { + fn is_registered(id: &u64) -> bool { + *id != 42u64 + } +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type UpdateOrigin = EnsureSignedBy; + type MaxInvulnerables = ConstU32<20>; + type CollatorId = ::AccountId; + type CollatorIdOf = IdentityCollator; + type CollatorRegistration = IsRegistered; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type Currency = Balances; +} + +sp_runtime::impl_opaque_keys! { + pub struct MockSessionKeys { + // a key for aura authoring + pub aura: UintAuthorityId, + } +} + +impl From for MockSessionKeys { + fn from(aura: sp_runtime::testing::UintAuthorityId) -> Self { + Self { aura } + } +} + +parameter_types! { + pub static SessionHandlerCollators: Vec = Vec::new(); + pub static SessionChangeBlock: u64 = 0; +} + +pub struct TestSessionHandler; +impl pallet_session::SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[UintAuthorityId::ID]; + fn on_genesis_session(keys: &[(u64, Ks)]) { + SessionHandlerCollators::set(keys.iter().map(|(a, _)| *a).collect::>()) + } + fn on_new_session(_: bool, keys: &[(u64, Ks)], _: &[(u64, Ks)]) { + SessionChangeBlock::set(System::block_number()); + SessionHandlerCollators::set(keys.iter().map(|(a, _)| *a).collect::>()) + } + fn on_before_session_ending() {} + fn on_disabled(_: u32) {} +} + +parameter_types! { + pub const Offset: u64 = 0; + pub const Period: u64 = 10; +} + +impl pallet_session::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = ::AccountId; + // we don't have stash and controller, thus we don't need the convert as well. + type ValidatorIdOf = IdentityCollator; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = Invulnerables; + type SessionHandler = TestSessionHandler; + type Keys = MockSessionKeys; + type WeightInfo = (); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + let invulnerables = vec![1, 2]; + + let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)]; + let keys = balances + .iter() + .map(|&(i, _)| { + ( + i, + i, + MockSessionKeys { + aura: UintAuthorityId(i), + }, + ) + }) + .collect::>(); + let session = pallet_session::GenesisConfig:: { keys }; + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + invulnerables::GenesisConfig:: { invulnerables } + .assimilate_storage(&mut t) + .unwrap(); + session.assimilate_storage(&mut t).unwrap(); + + t.into() +} + +pub fn initialize_to_block(n: u64) { + for i in System::block_number() + 1..=n { + System::set_block_number(i); + >::on_initialize(i); + } +} diff --git a/pallets/invulnerables/src/tests.rs b/pallets/invulnerables/src/tests.rs new file mode 100644 index 0000000..928aff0 --- /dev/null +++ b/pallets/invulnerables/src/tests.rs @@ -0,0 +1,147 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + mock::{ + initialize_to_block, new_test_ext, Invulnerables, RootAccount, RuntimeEvent, + RuntimeOrigin, System, Test, + }, + Error, + }, + frame_support::{assert_noop, assert_ok}, + sp_runtime::traits::BadOrigin, +}; + +#[test] +fn basic_setup_works() { + new_test_ext().execute_with(|| { + // genesis should sort input + assert_eq!(Invulnerables::invulnerables(), vec![1, 2]); + }); +} + +#[test] +fn add_invulnerable_works() { + new_test_ext().execute_with(|| { + initialize_to_block(1); + assert_eq!(Invulnerables::invulnerables(), vec![1, 2]); + let new = 3; + + // function runs + assert_ok!(Invulnerables::add_invulnerable( + RuntimeOrigin::signed(RootAccount::get()), + new + )); + + System::assert_last_event(RuntimeEvent::Invulnerables( + crate::Event::InvulnerableAdded { account_id: new }, + )); + + // same element cannot be added more than once + assert_noop!( + Invulnerables::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), new), + Error::::AlreadyInvulnerable + ); + + // new element is now part of the invulnerables list + assert!(Invulnerables::invulnerables().to_vec().contains(&new)); + + // cannot add with non-root + assert_noop!( + Invulnerables::add_invulnerable(RuntimeOrigin::signed(1), new), + BadOrigin + ); + }); +} + +#[test] +fn add_invulnerable_does_not_work_if_not_registered() { + new_test_ext().execute_with(|| { + initialize_to_block(1); + assert_eq!(Invulnerables::invulnerables(), vec![1, 2]); + let new = 42; + + assert_noop!( + Invulnerables::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), new), + Error::::NoKeysRegistered + ); + }); +} + +#[test] +fn invulnerable_limit_works() { + new_test_ext().execute_with(|| { + assert_eq!(Invulnerables::invulnerables(), vec![1, 2]); + + // MaxInvulnerables: u32 = 20 + for ii in 3..=21 { + if ii < 21 { + assert_ok!(Invulnerables::add_invulnerable( + RuntimeOrigin::signed(RootAccount::get()), + ii + )); + } else { + assert_noop!( + Invulnerables::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), ii), + Error::::TooManyInvulnerables + ); + } + } + let expected: Vec = (1..=20).collect(); + assert_eq!(Invulnerables::invulnerables(), expected); + }); +} + +#[test] +fn remove_invulnerable_works() { + new_test_ext().execute_with(|| { + initialize_to_block(1); + assert_eq!(Invulnerables::invulnerables(), vec![1, 2]); + + assert_ok!(Invulnerables::add_invulnerable( + RuntimeOrigin::signed(RootAccount::get()), + 4 + )); + assert_ok!(Invulnerables::add_invulnerable( + RuntimeOrigin::signed(RootAccount::get()), + 3 + )); + + assert_eq!(Invulnerables::invulnerables(), vec![1, 2, 4, 3]); + + assert_ok!(Invulnerables::remove_invulnerable( + RuntimeOrigin::signed(RootAccount::get()), + 2 + )); + + System::assert_last_event(RuntimeEvent::Invulnerables( + crate::Event::InvulnerableRemoved { account_id: 2 }, + )); + assert_eq!(Invulnerables::invulnerables(), vec![1, 4, 3]); + + // cannot remove invulnerable not in the list + assert_noop!( + Invulnerables::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 2), + Error::::NotInvulnerable + ); + + // cannot remove without privilege + assert_noop!( + Invulnerables::remove_invulnerable(RuntimeOrigin::signed(1), 3), + BadOrigin + ); + }); +} diff --git a/pallets/invulnerables/src/weights.rs b/pallets/invulnerables/src/weights.rs new file mode 100644 index 0000000..574193b --- /dev/null +++ b/pallets/invulnerables/src/weights.rs @@ -0,0 +1,195 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_invulnerables +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `girazoki-XPS-15-9530`, CPU: `13th Gen Intel(R) Core(TM) i9-13900H` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_invulnerables +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// tmp/pallet_invulnerables.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_invulnerables. +pub trait WeightInfo { + fn add_invulnerable(_b: u32) -> Weight; + fn remove_invulnerable(_b: u32) -> Weight; + fn new_session(_b: u32) -> Weight; + fn reward_invulnerable(_b: u32) -> Weight; +} + +/// Weights for pallet_invulnerables using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Session::NextKeys` (r:1 w:0) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Invulnerables::Invulnerables` (r:1 w:1) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 99]`. + fn add_invulnerable(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `549 + b * (36 ±0)` + // Estimated: `4687 + b * (37 ±0)` + // Minimum execution time: 14_073_000 picoseconds. + Weight::from_parts(17_124_910, 4687) + // Standard Error: 1_519 + .saturating_add(Weight::from_parts(76_594, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into())) + } + /// Storage: `Invulnerables::Invulnerables` (r:1 w:1) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 100]`. + fn remove_invulnerable(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `70 + b * (32 ±0)` + // Estimated: `4687` + // Minimum execution time: 8_623_000 picoseconds. + Weight::from_parts(10_574_224, 4687) + // Standard Error: 992 + .saturating_add(Weight::from_parts(52_490, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Invulnerables::Invulnerables` (r:1 w:0) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 100]`. + fn new_session(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `70 + r * (32 ±0)` + // Estimated: `4687` + // Minimum execution time: 7_295_000 picoseconds. + Weight::from_parts(7_742_784, 4687) + // Standard Error: 4_715 + .saturating_add(Weight::from_parts(105_985, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Invulnerables::Invulnerables` (r:1 w:0) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 100]`. + fn reward_invulnerable(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `218 + b * (33 ±0)` + // Estimated: `4687` + // Minimum execution time: 17_514_000 picoseconds. + Weight::from_parts(19_797_082, 4687) + // Standard Error: 1_701 + .saturating_add(Weight::from_parts(69_693, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `Session::NextKeys` (r:1 w:0) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Invulnerables::Invulnerables` (r:1 w:1) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 99]`. + fn add_invulnerable(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `549 + b * (36 ±0)` + // Estimated: `4687 + b * (37 ±0)` + // Minimum execution time: 14_073_000 picoseconds. + Weight::from_parts(17_124_910, 4687) + // Standard Error: 1_519 + .saturating_add(Weight::from_parts(76_594, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into())) + } + /// Storage: `Invulnerables::Invulnerables` (r:1 w:1) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 100]`. + fn remove_invulnerable(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `70 + b * (32 ±0)` + // Estimated: `4687` + // Minimum execution time: 8_623_000 picoseconds. + Weight::from_parts(10_574_224, 4687) + // Standard Error: 992 + .saturating_add(Weight::from_parts(52_490, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Invulnerables::Invulnerables` (r:1 w:0) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 100]`. + fn new_session(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `70 + r * (32 ±0)` + // Estimated: `4687` + // Minimum execution time: 7_295_000 picoseconds. + Weight::from_parts(7_742_784, 4687) + // Standard Error: 4_715 + .saturating_add(Weight::from_parts(105_985, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Invulnerables::Invulnerables` (r:1 w:0) + /// Proof: `Invulnerables::Invulnerables` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 100]`. + fn reward_invulnerable(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `218 + b * (33 ±0)` + // Estimated: `4687` + // Minimum execution time: 17_514_000 picoseconds. + Weight::from_parts(19_797_082, 4687) + // Standard Error: 1_701 + .saturating_add(Weight::from_parts(69_693, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/pooled-staking/Cargo.toml b/pallets/pooled-staking/Cargo.toml new file mode 100644 index 0000000..486a344 --- /dev/null +++ b/pallets/pooled-staking/Cargo.toml @@ -0,0 +1,79 @@ +[package] +name = "pallet-pooled-staking" +authors = { workspace = true } +description = "A staking pallet implemented using shares in pools" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +dp-core = { workspace = true } +log = { workspace = true } +serde = { workspace = true, optional = true } +tp-maths = { workspace = true } +tp-traits = { workspace = true } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } + +# Nimbus +nimbus-primitives = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +num-traits = { workspace = true } +pallet-balances = { workspace = true, features = [ "std" ] } +similar-asserts = { workspace = true } +sp-io = { workspace = true, features = [ "std" ] } + +[features] +default = [ "std" ] +std = [ + "dp-core/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "nimbus-primitives/std", + "pallet-balances/std", + "parity-scale-codec/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-maths/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "nimbus-primitives/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-maths/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "nimbus-primitives/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/pooled-staking/README.md b/pallets/pooled-staking/README.md new file mode 100644 index 0000000..ac68c15 --- /dev/null +++ b/pallets/pooled-staking/README.md @@ -0,0 +1,39 @@ +# Pooled staking pallet + +This pallet implements a Delegated Proof of Stake (DPoS) election system based +on a pool logic inspired from AMM Liquidity Pools, which provide computationally +efficient reward distribution. + +## Pool design + +A pool represent an amount of currency shared among many users, whom own some amount of shares +among a total share supply. Users can join or leave the pool, which both increase the total amount +of shared currency and the supply of shares such that each share keeps the same value. Rewards or +slashing are shared among all share holders by increasing/decreasing the total amount of shared +currency without changing the shares amounts or supply. This pool system can be used for any state a delegator can be in which they can receive rewards and/or be slashed. + +For each candidate there are 4 pools a delegator can be in: +- **Joining pool**: The delegator requested to start delegating for that candidate. However they must wait some + time before they are eligible to rewards, as it would otherwise allow to earn rewards for past sessions the + delegator was not yet contributing to the election of the candidate. Once the joining delay is elapsed the + delegator can convert their **joining shares** into **auto compounding shares** or **manual rewards shares** + (decided in advance so that anyone can trigger the convertion). +- **Auto compounding pool**: The delegator is eligible to rewards which are automatically compounded. This is + done by increasing the total amount of stake backing the pool without changing the amount of shares owned, which indirectly increase the value of each share. +- **Manual rewards pool**: The delegator is eligible to rewards which are kept out of the pool. It is based + around a counter of how much reward has been distributed per share since genesis. For each delegator is stored the value of the counter when they joined the pool or last claimed, such that it is possible to compute the amount of withdrawable rewards based on the amount of owned shares. Any change of the amount + of shares of a delegator (joining/leaving) requires to force claiming the rewards to keep the calculations + correct. +- **Leaving pool**: The delegator requested to stop delegating for that candidate. However they are still + accountable if the candidate is slashed until the end of the leaving delay. They no longer count towards + the candidate score nor are eligible to rewards. + +## Held currency + +To allow delegators to participate in other pallets such as democracy, their stake stays in their account and +is **held** by the staking pallet. However since reward distribution and slashing are made indirectly without +iterating over the set of delegators, the amount held in the account can mismatch the funds at stake. It means +rewards are distributed to an account dedicated to the staking pallet, and delegators can then call an +extrinsic to get their rewards transfered to their account (with an hold for auto compounding rewards). +For slashing, it requires anyone to call an extrinsic to transfer the slashed currency out of the +slashed delegators account. \ No newline at end of file diff --git a/pallets/pooled-staking/src/benchmarking.rs b/pallets/pooled-staking/src/benchmarking.rs new file mode 100644 index 0000000..6c90902 --- /dev/null +++ b/pallets/pooled-staking/src/benchmarking.rs @@ -0,0 +1,618 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#![cfg(feature = "runtime-benchmarks")] + +use {super::*, crate::Pallet as PooledStaking}; + +use { + crate::{ + pools::Pool, + traits::{IsCandidateEligible, Timer}, + HoldReason, + PendingOperationKey::{JoiningAutoCompounding, JoiningManualRewards}, + }, + frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}, + frame_support::{ + dispatch::RawOrigin, + traits::{ + fungible::{InspectHold, Mutate, MutateHold}, + tokens::{fungible::Balanced, Precision}, + Get, + }, + }, + frame_system::EventRecord, + sp_std::prelude::*, +}; + +/// Minimum collator candidate stake +fn min_candidate_stk() -> T::Balance { + <::MinimumSelfDelegation as Get>::get() +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +/// Create a funded user. +/// Extra + min_candidate_stk is total minted funds +/// Returns tuple (id, balance) +fn create_funded_user( + string: &'static str, + n: u32, + extra: T::Balance, +) -> (T::AccountId, T::Balance) { + const SEED: u32 = 0; + let user = account(string, n, SEED); + let min_candidate_stk = min_candidate_stk::(); + let total = min_candidate_stk + extra; + T::Currency::set_balance(&user, total); + (user, total) +} + +pub(crate) fn currency_issue( + amount: T::Balance, +) -> crate::CreditOf { + <::Currency as Balanced>::issue(amount) +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn request_delegate() -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1; + let (caller_candidate, _deposit_amount) = create_funded_user::( + "candidate", + USER_SEED, + min_candidate_stk::() * 10u32.into(), + ); + + let (caller_delegator, _deposit_amount) = create_funded_user::( + "delegator", + USER_SEED, + min_candidate_stk::() * 10u32.into(), + ); + + T::EligibleCandidatesFilter::make_candidate_eligible(&caller_candidate, true); + // self delegation + PooledStaking::::request_delegate( + RawOrigin::Signed(caller_candidate.clone()).into(), + caller_candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + // self delegation + PooledStaking::::request_delegate( + RawOrigin::Signed(caller_candidate.clone()).into(), + caller_candidate.clone(), + TargetPool::ManualRewards, + min_candidate_stk::(), + )?; + + let timer = T::JoiningRequestTimer::now(); + + T::JoiningRequestTimer::skip_to_elapsed(); + + PooledStaking::::execute_pending_operations( + RawOrigin::Signed(caller_candidate.clone()).into(), + vec![PendingOperationQuery { + delegator: caller_candidate.clone(), + operation: JoiningAutoCompounding { + candidate: caller_candidate.clone(), + at: timer.clone(), + }, + }], + )?; + + PooledStaking::::execute_pending_operations( + RawOrigin::Signed(caller_candidate.clone()).into(), + vec![PendingOperationQuery { + delegator: caller_candidate.clone(), + operation: JoiningManualRewards { + candidate: caller_candidate.clone(), + at: timer.clone(), + }, + }], + )?; + + // self delegation to have something in joining + PooledStaking::::request_delegate( + RawOrigin::Signed(caller_candidate.clone()).into(), + caller_candidate.clone(), + TargetPool::ManualRewards, + min_candidate_stk::(), + )?; + + // Worst case scenario is: we have already shares in both pools, and we delegate again + // but we delegate with a different account + #[extrinsic_call] + _( + RawOrigin::Signed(caller_delegator.clone()), + caller_candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::() * 2u32.into(), + ); + + // assert that it comes out sorted + assert_last_event::( + Event::RequestedDelegate { + candidate: caller_candidate.clone(), + delegator: caller_delegator, + pool: TargetPool::AutoCompounding, + pending: min_candidate_stk::() * 2u32.into(), + } + .into(), + ); + Ok(()) + } + + #[benchmark] + fn execute_pending_operations( + b: Linear<1, { T::EligibleCandidatesBufferSize::get() }>, + ) -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1000; + let (caller, _deposit_amount) = + create_funded_user::("caller", USER_SEED, min_candidate_stk::() * b.into()); + + let mut pending_operations = vec![]; + let mut candidates = vec![]; + + T::Currency::set_balance(&T::StakingAccount::get(), min_candidate_stk::()); + + let timer = T::JoiningRequestTimer::now(); + + // Create as many delegations as one can + for i in 0..b { + let (candidate, _deposit) = create_funded_user::( + "candidate", + USER_SEED - i - 1, + min_candidate_stk::() * 2u32.into(), + ); + T::EligibleCandidatesFilter::make_candidate_eligible(&candidate, true); + + // self delegation + PooledStaking::::request_delegate( + RawOrigin::Signed(caller.clone()).into(), + candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + pending_operations.push(PendingOperationQuery { + delegator: caller.clone(), + operation: JoiningAutoCompounding { + candidate: candidate.clone(), + at: timer.clone(), + }, + }); + candidates.push(candidate); + } + + T::JoiningRequestTimer::skip_to_elapsed(); + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), pending_operations); + + let last_candidate = &candidates[candidates.len() - 1]; + // assert that it comes out sorted + assert_last_event::( + Event::ExecutedDelegate { + candidate: last_candidate.clone(), + delegator: caller, + pool: TargetPool::AutoCompounding, + staked: min_candidate_stk::(), + released: 0u32.into(), + } + .into(), + ); + Ok(()) + } + + #[benchmark] + fn request_undelegate() -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1; + let (caller, _deposit_amount) = + create_funded_user::("caller", USER_SEED, min_candidate_stk::()); + + T::EligibleCandidatesFilter::make_candidate_eligible(&caller, true); + + PooledStaking::::request_delegate( + RawOrigin::Signed(caller.clone()).into(), + caller.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + let timer = T::JoiningRequestTimer::now(); + + T::JoiningRequestTimer::skip_to_elapsed(); + + PooledStaking::::execute_pending_operations( + RawOrigin::Signed(caller.clone()).into(), + vec![PendingOperationQuery { + delegator: caller.clone(), + operation: JoiningAutoCompounding { + candidate: caller.clone(), + at: timer.clone(), + }, + }], + )?; + + let stake_to_remove = min_candidate_stk::() / 2u32.into(); + + // We now have a working delegation, and we can request to undelegate + // This should take the candidate out from being eligible + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + caller.clone(), + TargetPool::AutoCompounding, + SharesOrStake::Stake(stake_to_remove), + ); + + // lets get the hold amount to know dust + let on_hold = T::Currency::balance_on_hold(&HoldReason::PooledStake.into(), &caller); + // dust gets released immediatly + let dust = min_candidate_stk::() - on_hold; + + // assert that it comes out sorted + // TODO: hardcoded numbers should dissapear + assert_last_event::( + Event::RequestedUndelegate { + candidate: caller.clone(), + delegator: caller, + from: TargetPool::AutoCompounding, + pending: stake_to_remove - dust, + released: dust, + } + .into(), + ); + Ok(()) + } + + #[benchmark] + fn claim_manual_rewards( + b: Linear<1, { T::EligibleCandidatesBufferSize::get() }>, + ) -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1000; + let (caller, _deposit_amount) = + create_funded_user::("caller", USER_SEED, min_candidate_stk::() * b.into()); + + let mut candidate_delegator = vec![]; + T::Currency::set_balance(&T::StakingAccount::get(), min_candidate_stk::()); + // Create as many delegations as one can + for i in 0..b { + let (candidate, _deposit) = create_funded_user::( + "candidate", + USER_SEED - i - 1, + min_candidate_stk::() * 2u32.into(), + ); + T::EligibleCandidatesFilter::make_candidate_eligible(&candidate, true); + + // self delegation + PooledStaking::::request_delegate( + RawOrigin::Signed(candidate.clone()).into(), + candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + PooledStaking::::request_delegate( + RawOrigin::Signed(caller.clone()).into(), + candidate.clone(), + TargetPool::ManualRewards, + min_candidate_stk::(), + )?; + + candidate_delegator.push((candidate.clone(), caller.clone())) + } + + let timer = T::JoiningRequestTimer::now(); + + T::JoiningRequestTimer::skip_to_elapsed(); + + // Set counter to simulate rewards. + let counter = 100u32; + // Execute as many pending operations as posible + for i in 0..b { + let candidate: T::AccountId = account("candidate", USER_SEED - i - 1, 0); + + PooledStaking::::execute_pending_operations( + RawOrigin::Signed(caller.clone()).into(), + vec![PendingOperationQuery { + delegator: caller.clone(), + operation: JoiningManualRewards { + candidate: candidate.clone(), + at: timer.clone(), + }, + }], + )?; + + crate::Pools::::set(candidate, &PoolsKey::ManualRewardsCounter, counter.into()); + } + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + candidate_delegator.clone(), + ); + + let (candidate, delegator) = &candidate_delegator[candidate_delegator.len() - 1]; + let shares = min_candidate_stk::() / T::InitialManualClaimShareValue::get(); + // We should have the last pairs event as the last event + assert_last_event::( + Event::ClaimedManualRewards { + candidate: candidate.clone(), + delegator: delegator.clone(), + rewards: shares * counter.into(), + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn rebalance_hold() -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1000; + let (caller, _deposit_amount) = + create_funded_user::("caller", USER_SEED, min_candidate_stk::() * 2u32.into()); + + T::Currency::set_balance(&T::StakingAccount::get(), min_candidate_stk::()); + // Create as many delegations as one can + + let (candidate, _deposit) = create_funded_user::( + "caller", + USER_SEED - 1, + min_candidate_stk::() * 2u32.into(), + ); + + let (caller_2, _deposit_amount) = create_funded_user::( + "caller", + USER_SEED - 2u32, + min_candidate_stk::() * 2u32.into(), + ); + + T::EligibleCandidatesFilter::make_candidate_eligible(&candidate, true); + // self delegation + PooledStaking::::request_delegate( + RawOrigin::Signed(candidate.clone()).into(), + candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + PooledStaking::::request_delegate( + RawOrigin::Signed(caller.clone()).into(), + candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + PooledStaking::::request_delegate( + RawOrigin::Signed(caller_2.clone()).into(), + candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + let fake_hold = min_candidate_stk::() / 2u32.into(); + + // We manually hack it such that hold!=stake + pools::Joining::::set_hold(&candidate, &caller, Stake(fake_hold)); + let on_hold_before = T::Currency::balance_on_hold(&HoldReason::PooledStake.into(), &caller); + T::Currency::release( + &HoldReason::PooledStake.into(), + &caller, + on_hold_before - fake_hold, + Precision::Exact, + )?; + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + candidate.clone(), + caller.clone(), + AllTargetPool::Joining, + ); + + // After this hold should have been rebalanced + let on_hold = T::Currency::balance_on_hold(&HoldReason::PooledStake.into(), &caller); + assert_eq!(on_hold, min_candidate_stk::()); + Ok(()) + } + + #[benchmark] + fn update_candidate_position( + b: Linear<1, { T::EligibleCandidatesBufferSize::get() }>, + ) -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1000; + let (caller, _deposit_amount) = + create_funded_user::("caller", USER_SEED, min_candidate_stk::()); + + T::Currency::set_balance(&T::StakingAccount::get(), min_candidate_stk::()); + let mut candidates = vec![]; + + // Create as many candidates as one can + for i in 0..b { + let (candidate, _deposit) = create_funded_user::( + "candidate", + USER_SEED - i - 1, + min_candidate_stk::() * 2u32.into(), + ); + + // self delegation + PooledStaking::::request_delegate( + RawOrigin::Signed(candidate.clone()).into(), + candidate.clone(), + TargetPool::AutoCompounding, + min_candidate_stk::(), + )?; + + // Make candidate eligible + T::EligibleCandidatesFilter::make_candidate_eligible(&candidate, true); + + candidates.push(candidate.clone()) + } + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), candidates); + + Ok(()) + } + + #[benchmark] + fn swap_pool() -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1; + + let source_stake = min_candidate_stk::() * 10u32.into(); + + let (caller, _deposit_amount) = create_funded_user::("caller", USER_SEED, source_stake); + + T::EligibleCandidatesFilter::make_candidate_eligible(&caller, true); + + PooledStaking::::request_delegate( + RawOrigin::Signed(caller.clone()).into(), + caller.clone(), + TargetPool::AutoCompounding, + source_stake, + )?; + + let timer = T::JoiningRequestTimer::now(); + + T::JoiningRequestTimer::skip_to_elapsed(); + + PooledStaking::::execute_pending_operations( + RawOrigin::Signed(caller.clone()).into(), + vec![PendingOperationQuery { + delegator: caller.clone(), + operation: JoiningAutoCompounding { + candidate: caller.clone(), + at: timer.clone(), + }, + }], + )?; + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + caller.clone(), + TargetPool::AutoCompounding, + SharesOrStake::Stake(source_stake), + ); + + let target_stake = source_stake; + let source_shares = crate::pools::AutoCompounding::::stake_to_shares_or_init( + &caller, + Stake(source_stake), + ) + .unwrap() + .0; + + let target_shares = + crate::pools::ManualRewards::::stake_to_shares_or_init(&caller, Stake(target_stake)) + .unwrap() + .0; + + assert_last_event::( + Event::SwappedPool { + candidate: caller.clone(), + delegator: caller, + source_pool: TargetPool::AutoCompounding, + source_shares, + source_stake, + target_shares, + target_stake, + pending_leaving: 0u32.into(), + released: 0u32.into(), + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn distribute_rewards() -> Result<(), BenchmarkError> { + const USER_SEED: u32 = 1; + + let source_stake = min_candidate_stk::() * 10u32.into(); + + let (caller, _deposit_amount) = + create_funded_user::("caller", USER_SEED, source_stake * 2u32.into()); + + T::EligibleCandidatesFilter::make_candidate_eligible(&caller, true); + + PooledStaking::::request_delegate( + RawOrigin::Signed(caller.clone()).into(), + caller.clone(), + TargetPool::AutoCompounding, + source_stake, + )?; + PooledStaking::::request_delegate( + RawOrigin::Signed(caller.clone()).into(), + caller.clone(), + TargetPool::ManualRewards, + source_stake, + )?; + + let timer = T::JoiningRequestTimer::now(); + + T::JoiningRequestTimer::skip_to_elapsed(); + + PooledStaking::::execute_pending_operations( + RawOrigin::Signed(caller.clone()).into(), + vec![ + PendingOperationQuery { + delegator: caller.clone(), + operation: JoiningAutoCompounding { + candidate: caller.clone(), + at: timer.clone(), + }, + }, + PendingOperationQuery { + delegator: caller.clone(), + operation: JoiningManualRewards { + candidate: caller.clone(), + at: timer.clone(), + }, + }, + ], + )?; + + T::Currency::mint_into(&T::StakingAccount::get(), source_stake).unwrap(); + + #[block] + { + crate::pools::distribute_rewards::(&caller, currency_issue::(source_stake))?; + } + + Ok(()) + } + + impl_benchmark_test_suite!( + PooledStaking, + crate::mock::ExtBuilder::default().build(), + crate::mock::Runtime, + ); +} diff --git a/pallets/pooled-staking/src/calls.rs b/pallets/pooled-staking/src/calls.rs new file mode 100644 index 0000000..e71ad06 --- /dev/null +++ b/pallets/pooled-staking/src/calls.rs @@ -0,0 +1,621 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + candidate::Candidates, + pools::{self, Pool}, + traits::Timer, + AllTargetPool, Candidate, Config, Delegator, Error, Event, HoldReason, Pallet, + PendingOperationKey, PendingOperationQuery, PendingOperationQueryOf, PendingOperations, + Shares, SharesOrStake, Stake, TargetPool, + }, + frame_support::{ + dispatch::DispatchErrorWithPostInfo, + pallet_prelude::*, + traits::{ + fungible::{Mutate, MutateHold}, + tokens::{Precision, Preservation}, + }, + }, + sp_runtime::traits::{CheckedSub, Zero}, + sp_std::vec::Vec, + tp_maths::{ErrAdd, ErrSub}, +}; + +pub struct Calls(PhantomData); + +impl Calls { + pub fn rebalance_hold( + candidate: Candidate, + delegator: Delegator, + pool: AllTargetPool, + ) -> DispatchResultWithPostInfo { + let (held, stake) = match pool { + AllTargetPool::Joining => { + let held = pools::Joining::::hold(&candidate, &delegator); + let shares = pools::Joining::::shares(&candidate, &delegator); + let stake = pools::Joining::::shares_to_stake(&candidate, shares)?; + pools::Joining::::set_hold(&candidate, &delegator, stake); + (held, stake) + } + AllTargetPool::AutoCompounding => { + let held = pools::AutoCompounding::::hold(&candidate, &delegator); + let shares = pools::AutoCompounding::::shares(&candidate, &delegator); + let stake = pools::AutoCompounding::::shares_to_stake(&candidate, shares)?; + pools::AutoCompounding::::set_hold(&candidate, &delegator, stake); + (held, stake) + } + AllTargetPool::ManualRewards => { + let held = pools::ManualRewards::::hold(&candidate, &delegator); + let shares = pools::ManualRewards::::shares(&candidate, &delegator); + let stake = pools::ManualRewards::::shares_to_stake(&candidate, shares)?; + pools::ManualRewards::::set_hold(&candidate, &delegator, stake); + (held, stake) + } + AllTargetPool::Leaving => { + let held = pools::Leaving::::hold(&candidate, &delegator); + let shares = pools::Leaving::::shares(&candidate, &delegator); + let stake = pools::Leaving::::shares_to_stake(&candidate, shares)?; + pools::Leaving::::set_hold(&candidate, &delegator, stake); + (held, stake) + } + }; + + if stake == held { + return Ok(().into()); + } + + if let Some(diff) = stake.0.checked_sub(&held.0) { + T::Currency::transfer( + &T::StakingAccount::get(), + &delegator, + diff, + Preservation::Preserve, + )?; + T::Currency::hold(&HoldReason::PooledStake.into(), &delegator, diff)?; + return Ok(().into()); + } + + if let Some(diff) = held.0.checked_sub(&stake.0) { + T::Currency::release( + &HoldReason::PooledStake.into(), + &delegator, + diff, + Precision::Exact, + )?; + T::Currency::transfer( + &delegator, + &T::StakingAccount::get(), + diff, + Preservation::Preserve, + )?; + return Ok(().into()); + } + + // should be unreachable as diff must either be positive or negative + Ok(().into()) + } + + pub fn request_delegate( + candidate: Candidate, + delegator: Delegator, + pool: TargetPool, + stake: T::Balance, + ) -> DispatchResultWithPostInfo { + ensure!(!stake.is_zero(), Error::::StakeMustBeNonZero); + + // Convert stake into joining shares quantity. + let shares = pools::Joining::::stake_to_shares_or_init(&candidate, Stake(stake))?; + + // If the amount was stake and is less than the value of 1 share it will round down to + // 0 share. We avoid doing any work for 0 shares. + ensure!(!shares.0.is_zero(), Error::::StakeMustBeNonZero); + + // We create the new joining shares. It returns the actual amount of stake those shares + // represents (due to rounding). + let stake = pools::Joining::::add_shares(&candidate, &delegator, shares)?; + + // We hold the funds of the delegator and register its stake into the candidate stake. + T::Currency::hold(&HoldReason::PooledStake.into(), &delegator, stake.0)?; + pools::Joining::::increase_hold(&candidate, &delegator, &stake)?; + Candidates::::add_total_stake(&candidate, &stake)?; + + // We create/mutate a request for joining. + let now = T::JoiningRequestTimer::now(); + let operation_key = match pool { + TargetPool::AutoCompounding => PendingOperationKey::JoiningAutoCompounding { + candidate: candidate.clone(), + at: now, + }, + TargetPool::ManualRewards => PendingOperationKey::JoiningManualRewards { + candidate: candidate.clone(), + at: now, + }, + }; + + // We store/mutate the operation in storage. + let operation = PendingOperations::::get(&delegator, &operation_key); + let operation = operation + .err_add(&shares.0) + .map_err(|_| Error::::MathOverflow)?; + PendingOperations::::set(&delegator, &operation_key, operation); + + pools::check_candidate_consistency::(&candidate)?; + + Pallet::::deposit_event(Event::::RequestedDelegate { + candidate, + delegator, + pool, + pending: stake.0, + }); + + Ok(().into()) + } + + pub fn request_undelegate( + candidate: Candidate, + delegator: Delegator, + pool: TargetPool, + amount: SharesOrStake, + ) -> DispatchResultWithPostInfo { + // Converts amount to shares of the correct pool + let shares = match (amount, pool) { + (SharesOrStake::Shares(s), _) => s, + (SharesOrStake::Stake(s), TargetPool::AutoCompounding) => { + pools::AutoCompounding::::stake_to_shares(&candidate, Stake(s))?.0 + } + (SharesOrStake::Stake(s), TargetPool::ManualRewards) => { + pools::ManualRewards::::stake_to_shares(&candidate, Stake(s))?.0 + } + }; + + // Any change in the amount of Manual Rewards shares requires to claim manual rewards. + if let TargetPool::ManualRewards = pool { + Self::claim_manual_rewards(&[(candidate.clone(), delegator.clone())])?; + } + + // Destroy shares + let removed_stake = Self::destroy_shares(&candidate, &delegator, pool, Shares(shares))?; + + // All this stake no longer contribute to the election of the candidate. + Candidates::::sub_total_stake(&candidate, removed_stake)?; + + // We proceed with the leaving, which create Leaving shares and request, + // and release the dust from the convertion to Leaving shares. + let (leaving_stake, dust) = Self::leave_stake(&candidate, &delegator, removed_stake)?; + + pools::check_candidate_consistency::(&candidate)?; + + Pallet::::deposit_event(Event::::RequestedUndelegate { + candidate, + delegator, + from: pool, + pending: leaving_stake.0, + released: dust.0, + }); + + Ok(().into()) + } + + pub fn execute_pending_operations( + operations: Vec>, + ) -> DispatchResultWithPostInfo { + for (index, query) in operations.into_iter().enumerate() { + // We deconstruct the query and find the balance associated with it. + // If it is zero it may not exist or have been executed before, thus + // we simply skip it instead of erroring. + let PendingOperationQuery { + delegator, + operation, + } = query; + + let value = PendingOperations::::get(&delegator, &operation); + + if value.is_zero() { + continue; + } + + match &operation { + PendingOperationKey::JoiningAutoCompounding { candidate, at } => { + ensure!( + T::JoiningRequestTimer::is_elapsed(at), + Error::::RequestCannotBeExecuted(index as u16) + ); + + Self::execute_joining( + candidate.clone(), + delegator.clone(), + TargetPool::AutoCompounding, + Shares(value), + )?; + } + PendingOperationKey::JoiningManualRewards { candidate, at } => { + ensure!( + T::JoiningRequestTimer::is_elapsed(at), + Error::::RequestCannotBeExecuted(index as u16) + ); + + Self::execute_joining( + candidate.clone(), + delegator.clone(), + TargetPool::ManualRewards, + Shares(value), + )?; + } + PendingOperationKey::Leaving { candidate, at } => { + ensure!( + T::LeavingRequestTimer::is_elapsed(at), + Error::::RequestCannotBeExecuted(index as u16) + ); + + Self::execute_leaving(candidate.clone(), delegator.clone(), Shares(value))?; + } + } + + PendingOperations::::remove(&delegator, &operation); + } + + Ok(().into()) + } + + fn execute_joining( + candidate: Candidate, + delegator: Delegator, + pool: TargetPool, + joining_shares: Shares, + ) -> DispatchResultWithPostInfo { + // Convert joining shares into stake. + let stake = pools::Joining::::sub_shares(&candidate, &delegator, joining_shares)?; + + // No rewards are distributed to the Joining pools, so there should always + // be enough hold. Thus no need to rebalance. + pools::Joining::::decrease_hold(&candidate, &delegator, &stake)?; + + // Any change in the amount of Manual Rewards shares requires to claim manual rewards. + if let TargetPool::ManualRewards = pool { + Self::claim_manual_rewards(&[(candidate.clone(), delegator.clone())])?; + } + + // Convert stake into shares quantity. + let shares = match pool { + TargetPool::AutoCompounding => { + pools::AutoCompounding::::stake_to_shares_or_init(&candidate, stake)? + } + TargetPool::ManualRewards => { + pools::ManualRewards::::stake_to_shares_or_init(&candidate, stake)? + } + }; + + // If stake doesn't allow to get at least one share we release all the funds. + if shares.0.is_zero() { + T::Currency::release( + &HoldReason::PooledStake.into(), + &delegator, + stake.0, + Precision::Exact, + )?; + Candidates::::sub_total_stake(&candidate, Stake(stake.0))?; + pools::check_candidate_consistency::(&candidate)?; + return Ok(().into()); + } + + // We create the new shares. It returns the actual amount of stake those shares + // represents (due to rounding). + let actually_staked = match pool { + TargetPool::AutoCompounding => { + let stake = + pools::AutoCompounding::::add_shares(&candidate, &delegator, shares)?; + pools::AutoCompounding::::increase_hold(&candidate, &delegator, &stake)?; + stake + } + TargetPool::ManualRewards => { + let stake = pools::ManualRewards::::add_shares(&candidate, &delegator, shares)?; + pools::ManualRewards::::increase_hold(&candidate, &delegator, &stake)?; + stake + } + }; + + // We release currency that couldn't be converted to shares due to rounding. + // This thus can reduce slighly the total stake of the candidate. + let release = stake + .0 + .err_sub(&actually_staked.0) + .map_err(|_| Error::::MathUnderflow)?; + T::Currency::release( + &HoldReason::PooledStake.into(), + &delegator, + release, + Precision::Exact, + )?; + Candidates::::sub_total_stake(&candidate, Stake(release))?; + + // Events + let event = match pool { + TargetPool::AutoCompounding => Event::::StakedAutoCompounding { + candidate: candidate.clone(), + delegator: delegator.clone(), + shares: shares.0, + stake: actually_staked.0, + }, + TargetPool::ManualRewards => Event::::StakedManualRewards { + candidate: candidate.clone(), + delegator: delegator.clone(), + shares: shares.0, + stake: actually_staked.0, + }, + }; + + pools::check_candidate_consistency::(&candidate)?; + + Pallet::::deposit_event(event); + Pallet::::deposit_event(Event::::ExecutedDelegate { + candidate, + delegator, + pool, + staked: actually_staked.0, + released: release, + }); + + Ok(().into()) + } + + fn execute_leaving( + candidate: Candidate, + delegator: Delegator, + leavinig_shares: Shares, + ) -> DispatchResultWithPostInfo { + // Convert leaving shares into stake. + let stake = pools::Leaving::::sub_shares(&candidate, &delegator, leavinig_shares)?; + + // No rewards are distributed to the Leaving pools, so there should always + // be enough hold. Thus no need to rebalance. + pools::Leaving::::decrease_hold(&candidate, &delegator, &stake)?; + + // We release the funds and consider them unstaked. + T::Currency::release( + &HoldReason::PooledStake.into(), + &delegator, + stake.0, + Precision::Exact, + )?; + + Pallet::::deposit_event(Event::::ExecutedUndelegate { + candidate, + delegator, + released: stake.0, + }); + + Ok(().into()) + } + + pub fn claim_manual_rewards( + pairs: &[(Candidate, Delegator)], + ) -> DispatchResultWithPostInfo { + for (candidate, delegator) in pairs { + let Stake(rewards) = pools::ManualRewards::::claim_rewards(candidate, delegator)?; + + if rewards.is_zero() { + continue; + } + + T::Currency::transfer( + &T::StakingAccount::get(), + delegator, + rewards, + Preservation::Preserve, + )?; + + Pallet::::deposit_event(Event::::ClaimedManualRewards { + candidate: candidate.clone(), + delegator: delegator.clone(), + rewards, + }); + } + + Ok(().into()) + } + + pub fn update_candidate_position(candidates: &[Candidate]) -> DispatchResultWithPostInfo { + for candidate in candidates { + let stake = Candidates::::total_stake(candidate); + Candidates::::update_total_stake(candidate, stake)?; + } + + Ok(().into()) + } + + pub fn swap_pool( + candidate: Candidate, + delegator: Delegator, + source_pool: TargetPool, + amount: SharesOrStake, + ) -> DispatchResultWithPostInfo { + // Converts amount to shares of the correct pool + let old_shares = match (amount, source_pool) { + (SharesOrStake::Shares(s), _) => s, + (SharesOrStake::Stake(s), TargetPool::AutoCompounding) => { + pools::AutoCompounding::::stake_to_shares(&candidate, Stake(s))?.0 + } + (SharesOrStake::Stake(s), TargetPool::ManualRewards) => { + pools::ManualRewards::::stake_to_shares(&candidate, Stake(s))?.0 + } + }; + + // As it will either move in or out of the ManualRewards pool, manual rewards + // needs to be claimed. + Self::claim_manual_rewards(&[(candidate.clone(), delegator.clone())])?; + + // Destroy shares from the old pool. + let removed_stake = + Self::destroy_shares(&candidate, &delegator, source_pool, Shares(old_shares))?; + + // Convert removed amount to new pool shares. + let new_shares = match source_pool { + TargetPool::AutoCompounding => { + pools::ManualRewards::::stake_to_shares_or_init(&candidate, removed_stake)? + } + TargetPool::ManualRewards => { + pools::AutoCompounding::::stake_to_shares_or_init(&candidate, removed_stake)? + } + }; + + ensure!(!new_shares.0.is_zero(), Error::::SwapResultsInZeroShares); + + // We create new shares in the new pool. It returns the actual amount of stake those shares + // represents (due to rounding). + let actually_staked = match source_pool { + TargetPool::ManualRewards => { + let stake = + pools::AutoCompounding::::add_shares(&candidate, &delegator, new_shares)?; + pools::AutoCompounding::::increase_hold(&candidate, &delegator, &stake)?; + stake + } + TargetPool::AutoCompounding => { + let stake = + pools::ManualRewards::::add_shares(&candidate, &delegator, new_shares)?; + pools::ManualRewards::::increase_hold(&candidate, &delegator, &stake)?; + stake + } + }; + + let stake_decrease = removed_stake + .0 + .err_sub(&actually_staked.0) + .map_err(Error::::from)?; + + // The left-over no longer contribute to the election of the candidate. + Candidates::::sub_total_stake(&candidate, Stake(stake_decrease))?; + + // We proceed with the leaving, which create Leaving shares and request, + // and release the dust from the convertion to Leaving shares. + let (leaving_stake, dust) = if stake_decrease.is_zero() { + (Stake(0u32.into()), Stake(0u32.into())) + } else { + Self::leave_stake(&candidate, &delegator, Stake(stake_decrease))? + }; + + pools::check_candidate_consistency::(&candidate)?; + + Pallet::::deposit_event(Event::::SwappedPool { + candidate: candidate.clone(), + delegator: delegator.clone(), + source_pool, + source_shares: old_shares, + source_stake: removed_stake.0, + target_shares: new_shares.0, + target_stake: actually_staked.0, + pending_leaving: leaving_stake.0, + released: dust.0, + }); + + Ok(().into()) + } + + /// Destory ManualReward or AutoCompounding shares while performing hold rebalancing if + /// necessary. + fn destroy_shares( + candidate: &Candidate, + delegator: &Delegator, + pool: TargetPool, + shares: Shares, + ) -> Result, DispatchErrorWithPostInfo> { + match pool { + TargetPool::AutoCompounding => { + let stake = pools::AutoCompounding::::shares_to_stake(candidate, shares)?; + + if stake.0 > pools::AutoCompounding::::hold(candidate, delegator).0 { + Self::rebalance_hold( + candidate.clone(), + delegator.clone(), + AllTargetPool::AutoCompounding, + )?; + } + + // This should be the same `stake` as before. + let stake = pools::AutoCompounding::::sub_shares(candidate, delegator, shares)?; + + pools::AutoCompounding::::decrease_hold(candidate, delegator, &stake)?; + Ok(stake) + } + TargetPool::ManualRewards => { + let stake = pools::ManualRewards::::shares_to_stake(candidate, shares)?; + + if stake.0 > pools::ManualRewards::::hold(candidate, delegator).0 { + Self::rebalance_hold( + candidate.clone(), + delegator.clone(), + AllTargetPool::ManualRewards, + )?; + } + + // This should be the same `stake` as before. + let stake = pools::ManualRewards::::sub_shares(candidate, delegator, shares)?; + + pools::ManualRewards::::decrease_hold(candidate, delegator, &stake)?; + Ok(stake) + } + } + } + + /// Perform the leaving proceduce with provided stake, which will create + /// Leaving shares and request, and release the rounding dust. It DOES NOT + /// destroy shares in other pools. + /// Returns a tuple of the amount of stake in the leaving pool and the dust + /// that was released. + fn leave_stake( + candidate: &Candidate, + delegator: &Delegator, + stake: Stake, + ) -> Result<(Stake, Stake), DispatchErrorWithPostInfo> { + // Create leaving shares. + // As with all pools there will be some rounding error, this amount + // should be small enough so that it is safe to directly release it + // in the delegator account. + let leaving_shares = pools::Leaving::::stake_to_shares_or_init(candidate, stake)?; + let leaving_stake = pools::Leaving::::add_shares(candidate, delegator, leaving_shares)?; + pools::Leaving::::increase_hold(candidate, delegator, &leaving_stake)?; + + // We create/mutate a request for leaving. + let now = T::LeavingRequestTimer::now(); + let operation_key = PendingOperationKey::Leaving { + candidate: candidate.clone(), + at: now, + }; + let operation = PendingOperations::::get(delegator, &operation_key); + let operation = operation + .err_add(&leaving_shares.0) + .map_err(|_| Error::::MathOverflow)?; + PendingOperations::::set(delegator, &operation_key, operation); + + // We release the dust if non-zero. + let dust = stake + .0 + .err_sub(&leaving_stake.0) + .map_err(Error::::from)?; + + if !dust.is_zero() { + T::Currency::release( + &HoldReason::PooledStake.into(), + delegator, + dust, + Precision::Exact, + )?; + } + + Ok((leaving_stake, Stake(dust))) + } +} diff --git a/pallets/pooled-staking/src/candidate.rs b/pallets/pooled-staking/src/candidate.rs new file mode 100644 index 0000000..13d9aed --- /dev/null +++ b/pallets/pooled-staking/src/candidate.rs @@ -0,0 +1,202 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + pools::{self, Pool}, + traits::IsCandidateEligible, + Candidate, Config, Error, Event, Pallet, Pools, PoolsKey, SortedEligibleCandidates, Stake, + }, + core::{cmp::Ordering, marker::PhantomData}, + parity_scale_codec::{Decode, Encode}, + scale_info::TypeInfo, + sp_core::{Get, RuntimeDebug}, + sp_runtime::traits::Zero, + tp_maths::{ErrAdd, ErrSub}, +}; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +/// Eligible candidate with its stake. +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Clone, TypeInfo)] +pub struct EligibleCandidate { + pub candidate: C, + pub stake: S, +} + +impl Ord for EligibleCandidate { + fn cmp(&self, other: &Self) -> Ordering { + self.stake + .cmp(&other.stake) + .reverse() + .then_with(|| self.candidate.cmp(&other.candidate)) + } +} + +impl PartialOrd for EligibleCandidate { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +pub struct Candidates(PhantomData); + +impl Candidates { + pub fn total_stake(candidate: &Candidate) -> Stake { + Stake(Pools::::get(candidate, &PoolsKey::CandidateTotalStake)) + } + + pub fn add_total_stake( + candidate: &Candidate, + stake: &Stake, + ) -> Result<(), Error> { + if stake.0.is_zero() { + return Ok(()); + } + + let new_stake = Self::total_stake(candidate).0.err_add(&stake.0)?; + + Pallet::::deposit_event(Event::::IncreasedStake { + candidate: candidate.clone(), + stake_diff: stake.0, + }); + + Self::update_total_stake(candidate, Stake(new_stake))?; + + Ok(()) + } + + pub fn sub_total_stake( + candidate: &Candidate, + stake: Stake, + ) -> Result<(), Error> { + if stake.0.is_zero() { + return Ok(()); + } + + let new_stake = Self::total_stake(candidate).0.err_sub(&stake.0)?; + + Pallet::::deposit_event(Event::::DecreasedStake { + candidate: candidate.clone(), + stake_diff: stake.0, + }); + + Self::update_total_stake(candidate, Stake(new_stake))?; + + Ok(()) + } + + pub fn update_total_stake( + candidate: &Candidate, + new_stake: Stake, + ) -> Result<(), Error> { + let stake_before = Pools::::get(candidate, &PoolsKey::CandidateTotalStake); + Pools::::set(candidate, &PoolsKey::CandidateTotalStake, new_stake.0); + + // Compute self delegation. + let ac_self = if pools::AutoCompounding::::shares_supply(candidate) + .0 + .is_zero() + { + Zero::zero() + } else { + let shares = pools::AutoCompounding::::shares(candidate, candidate); + pools::AutoCompounding::shares_to_stake(candidate, shares)?.0 + }; + + let mr_self = if pools::ManualRewards::::shares_supply(candidate) + .0 + .is_zero() + { + Zero::zero() + } else { + let shares = pools::ManualRewards::::shares(candidate, candidate); + pools::ManualRewards::shares_to_stake(candidate, shares)?.0 + }; + + let joining_self = if pools::Joining::::shares_supply(candidate).0.is_zero() { + Zero::zero() + } else { + let shares = pools::Joining::::shares(candidate, candidate); + pools::Joining::shares_to_stake(candidate, shares)?.0 + }; + + let self_delegation = ac_self.err_add(&mr_self)?.err_add(&joining_self)?; + + let mut list = SortedEligibleCandidates::::get(); + + // Remove old data if it exists. + let old_position = match list.binary_search(&EligibleCandidate { + candidate: candidate.clone(), + stake: stake_before, + }) { + Ok(pos) => { + let _ = list.remove(pos); + Some(pos as u32) + } + Err(_) => None, + }; + + let eligible = self_delegation >= T::MinimumSelfDelegation::get() + && T::EligibleCandidatesFilter::is_candidate_eligible(candidate); + + // Find new position in the sorted list. + // It will not be inserted if under the minimum self delegation. + let new_position = if eligible { + let entry = EligibleCandidate { + candidate: candidate.clone(), + stake: new_stake.0, + }; + + // Candidate should not appear in the list, we're instead searching where + // to insert it. + let Err(pos) = list.binary_search(&entry) else { + return Err(Error::::InconsistentState); + }; + + if pos >= T::EligibleCandidatesBufferSize::get() as usize { + None + } else { + // Insert in correct position then truncate the list if necessary. + list = list + .try_mutate(move |list| { + list.insert(pos, entry.clone()); + list.truncate(T::EligibleCandidatesBufferSize::get() as usize) + }) + // This should not occur as we truncate the list above. + .ok_or(Error::::InconsistentState)?; + + Some(pos as u32) + } + } else { + None + }; + + Pallet::::deposit_event(Event::::UpdatedCandidatePosition { + candidate: candidate.clone(), + stake: new_stake.0, + self_delegation, + before: old_position, + after: new_position, + }); + + SortedEligibleCandidates::::set(list); + + Ok(()) + } +} diff --git a/pallets/pooled-staking/src/lib.rs b/pallets/pooled-staking/src/lib.rs new file mode 100644 index 0000000..c638df2 --- /dev/null +++ b/pallets/pooled-staking/src/lib.rs @@ -0,0 +1,640 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! A staking pallet based on pools of shares. +//! +//! This pallet works with pools inspired by AMM liquidity pools to easily distribute +//! rewards with support for both non-compounding and compounding rewards. +//! +//! Each candidate internally have 3 pools: +//! - a pool for all delegators willing to auto compound. +//! - a pool for all delegators not willing to auto compound. +//! - a pool for all delegators that are in the process of removing stake. +//! +//! When delegating the funds of the delegator are reserved, and shares allow to easily +//! distribute auto compounding rewards (by simply increasing the total shared amount) +//! and easily slash (each share loose part of its value). Rewards are distributed to an account +//! id dedicated to the staking pallet, and delegators can call an extrinsic to transfer their rewards +//! to their own account (but as reserved). Keeping funds reserved in user accounts allow them to +//! participate in other processes such as gouvernance. + +#![cfg_attr(not(feature = "std"), no_std)] + +mod calls; +mod candidate; +mod pools; +pub mod traits; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub mod weights; +use frame_support::pallet; +pub use weights::WeightInfo; + +pub use {candidate::EligibleCandidate, pallet::*}; + +#[pallet] +pub mod pallet { + use { + super::*, + crate::{ + traits::{IsCandidateEligible, Timer}, + weights::WeightInfo, + }, + calls::Calls, + core::marker::PhantomData, + frame_support::{ + pallet_prelude::*, + storage::types::{StorageDoubleMap, StorageValue, ValueQuery}, + traits::{fungible, tokens::Balance, IsType}, + Blake2_128Concat, + }, + frame_system::pallet_prelude::*, + parity_scale_codec::{Decode, Encode, FullCodec}, + scale_info::TypeInfo, + sp_core::Get, + sp_runtime::{BoundedVec, Perbill}, + sp_std::vec::Vec, + tp_maths::MulDiv, + }; + + /// A reason for this pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + PooledStake, + } + + #[cfg(feature = "std")] + use serde::{Deserialize, Serialize}; + + // Type aliases for better readability. + pub type Candidate = ::AccountId; + pub type CreditOf = + fungible::Credit<::AccountId, ::Currency>; + pub type Delegator = ::AccountId; + + /// Key used by the `Pools` StorageDoubleMap, avoiding lots of maps. + /// StorageDoubleMap first key is the account id of the candidate. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Clone, TypeInfo)] + pub enum PoolsKey { + /// Total amount of currency backing this candidate across all pools. + CandidateTotalStake, + + /// Amount of joining shares a delegator have for that candidate. + JoiningShares { delegator: A }, + /// Total amount of joining shares existing for that candidate. + JoiningSharesSupply, + /// Amount of currency backing all the joining shares of that candidate. + JoiningSharesTotalStaked, + /// Amount of currency held in the delegator account. + JoiningSharesHeldStake { delegator: A }, + + /// Amount of auto compounding shares a delegator have for that candidate. + AutoCompoundingShares { delegator: A }, + /// Total amount of auto compounding shares existing for that candidate. + AutoCompoundingSharesSupply, + /// Amount of currency backing all the auto compounding shares of that candidate. + AutoCompoundingSharesTotalStaked, + /// Amount of currency held in the delegator account. + AutoCompoundingSharesHeldStake { delegator: A }, + + /// Amount of manual rewards shares a delegator have for that candidate. + ManualRewardsShares { delegator: A }, + /// Total amount of manual rewards shares existing for that candidate. + ManualRewardsSharesSupply, + /// Amount of currency backing all the manual rewards shares of that candidate. + ManualRewardsSharesTotalStaked, + /// Amount of currency held in the delegator account. + ManualRewardsSharesHeldStake { delegator: A }, + /// Counter of the cumulated rewards per share generated by that candidate since genesis. + /// Is safe to wrap around the maximum value of the balance type. + ManualRewardsCounter, + /// Value of the counter at the last time the delegator claimed its rewards or changed its amount of shares + /// (changing the amount of shares automatically claims pending rewards). + /// The difference between the checkpoint and the counter is the amount of claimable reward per share for + /// that delegator. + ManualRewardsCheckpoint { delegator: A }, + + /// Amount of shares of that delegator in the leaving pool of that candidate. + /// When leaving delegating funds are placed in the leaving pool until the leaving period is elapsed. + /// While in the leaving pool the funds are still slashable. + LeavingShares { delegator: A }, + /// Total amount of leaving shares existing for that candidate. + LeavingSharesSupply, + /// Amount of currency backing all the leaving shares of that candidate. + LeavingSharesTotalStaked, + /// Amount of currency held in the delegator account. + LeavingSharesHeldStake { delegator: A }, + } + + /// Key used by the "PendingOperations" StorageDoubleMap. + /// StorageDoubleMap first key is the account id of the delegator who made the request. + /// Value is the amount of shares in the joining/leaving pool. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Clone, TypeInfo)] + pub enum PendingOperationKey { + /// Candidate requested to join the auto compounding pool of a candidate. + JoiningAutoCompounding { candidate: A, at: J }, + /// Candidate requested to join the manual rewards pool of a candidate. + JoiningManualRewards { candidate: A, at: J }, + /// Candidate requested to to leave a pool of a candidate. + Leaving { candidate: A, at: L }, + } + + pub type PendingOperationKeyOf = PendingOperationKey< + ::AccountId, + <::JoiningRequestTimer as Timer>::Instant, + <::LeavingRequestTimer as Timer>::Instant, + >; + + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Clone, TypeInfo)] + pub struct PendingOperationQuery { + pub delegator: A, + pub operation: PendingOperationKey, + } + + pub type PendingOperationQueryOf = PendingOperationQuery< + ::AccountId, + <::JoiningRequestTimer as Timer>::Instant, + <::LeavingRequestTimer as Timer>::Instant, + >; + + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Copy, Clone, TypeInfo)] + pub enum TargetPool { + AutoCompounding, + ManualRewards, + } + + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Copy, Clone, TypeInfo)] + pub enum AllTargetPool { + Joining, + AutoCompounding, + ManualRewards, + Leaving, + } + + impl From for AllTargetPool { + fn from(value: TargetPool) -> Self { + match value { + TargetPool::AutoCompounding => AllTargetPool::AutoCompounding, + TargetPool::ManualRewards => AllTargetPool::ManualRewards, + } + } + } + + /// Allow calls to be performed using either share amounts or stake. + /// When providing stake, calls will convert them into share amounts that are + /// worth up to the provided stake. The amount of stake thus will be at most the provided + /// amount. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Clone, TypeInfo)] + pub enum SharesOrStake { + Shares(T), + Stake(T), + } + + /// Wrapper type for an amount of shares. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, Default, PartialEq, Eq, Encode, Decode, Copy, Clone, TypeInfo)] + pub struct Shares(pub T); + + /// Wrapper type for an amount of staked currency. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, Default, PartialEq, Eq, Encode, Decode, Copy, Clone, TypeInfo)] + pub struct Stake(pub T); + + /// Pooled Staking pallet. + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Overarching event type + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The currency type. + /// Shares will use the same Balance type. + type Currency: fungible::Inspect + + fungible::Mutate + + fungible::Balanced + + fungible::MutateHold; + + /// Same as Currency::Balance. Must impl `MulDiv` which perform + /// multiplication followed by division using a bigger type to avoid + /// overflows. + type Balance: Balance + MulDiv; + + /// Account holding Currency of all delegators. + #[pallet::constant] + type StakingAccount: Get; + + /// When creating the first Shares for a candidate the supply can be arbitrary. + /// Picking a value too low will make an higher supply, which means each share will get + /// less rewards, and rewards calculations will have more impactful rounding errors. + /// Picking a value too high is a barrier of entry for staking. + #[pallet::constant] + type InitialManualClaimShareValue: Get; + /// When creating the first Shares for a candidate the supply can arbitrary. + /// Picking a value too high is a barrier of entry for staking, which will increase overtime + /// as the value of each share will increase due to auto compounding. + #[pallet::constant] + type InitialAutoCompoundingShareValue: Get; + + /// Minimum amount of stake a Candidate must delegate (stake) towards itself. Not reaching + /// this minimum prevents from being elected. + #[pallet::constant] + type MinimumSelfDelegation: Get; + /// Part of the rewards that will be sent exclusively to the collator. + #[pallet::constant] + type RewardsCollatorCommission: Get; + + /// The overarching runtime hold reason. + type RuntimeHoldReason: From; + + /// Condition for when a joining request can be executed. + type JoiningRequestTimer: Timer; + /// Condition for when a leaving request can be executed. + type LeavingRequestTimer: Timer; + /// All eligible candidates are stored in a sorted list that is modified each time + /// delegations changes. It is safer to bound this list, in which case eligible candidate + /// could fall out of this list if they have less stake than the top `EligibleCandidatesBufferSize` + /// eligible candidates. One of this top candidates leaving will then not bring the dropped candidate + /// in the list. An extrinsic is available to manually bring back such dropped candidate. + #[pallet::constant] + type EligibleCandidatesBufferSize: Get; + /// Additional filter for candidates to be eligible. + type EligibleCandidatesFilter: IsCandidateEligible; + + type WeightInfo: WeightInfo; + } + + /// Keeps a list of all eligible candidates, sorted by the amount of stake backing them. + /// This can be quickly updated using a binary search, and allow to easily take the top + /// `MaxCollatorSetSize`. + #[pallet::storage] + pub type SortedEligibleCandidates = StorageValue< + _, + BoundedVec< + candidate::EligibleCandidate, T::Balance>, + T::EligibleCandidatesBufferSize, + >, + ValueQuery, + >; + + /// Pools balances. + #[pallet::storage] + pub type Pools = StorageDoubleMap< + _, + Blake2_128Concat, + Candidate, + Blake2_128Concat, + PoolsKey, + T::Balance, + ValueQuery, + >; + + /// Pending operations balances. + /// Balances are expressed in joining/leaving shares amounts. + #[pallet::storage] + pub type PendingOperations = StorageDoubleMap< + _, + Blake2_128Concat, + Delegator, + Blake2_128Concat, + PendingOperationKeyOf, + T::Balance, + ValueQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Stake of the candidate has changed, which may have modified its + /// position in the eligible candidates list. + UpdatedCandidatePosition { + candidate: Candidate, + stake: T::Balance, + self_delegation: T::Balance, + before: Option, + after: Option, + }, + + /// User requested to delegate towards a candidate. + RequestedDelegate { + candidate: Candidate, + delegator: Delegator, + pool: TargetPool, + pending: T::Balance, + }, + /// Delegation request was executed. `staked` has been properly staked + /// in `pool`, while the rounding when converting to shares has been + /// `released`. + ExecutedDelegate { + candidate: Candidate, + delegator: Delegator, + pool: TargetPool, + staked: T::Balance, + released: T::Balance, + }, + /// User requested to undelegate from a candidate. + /// Stake was removed from a `pool` and is `pending` for the request + /// to be executed. The rounding when converting to leaving shares has + /// been `released` immediately. + RequestedUndelegate { + candidate: Candidate, + delegator: Delegator, + from: TargetPool, + pending: T::Balance, + released: T::Balance, + }, + /// Undelegation request was executed. + ExecutedUndelegate { + candidate: Candidate, + delegator: Delegator, + released: T::Balance, + }, + + /// Stake of that Candidate increased. + IncreasedStake { + candidate: Candidate, + stake_diff: T::Balance, + }, + /// Stake of that Candidate decreased. + DecreasedStake { + candidate: Candidate, + stake_diff: T::Balance, + }, + /// Delegator staked towards a Candidate for AutoCompounding Shares. + StakedAutoCompounding { + candidate: Candidate, + delegator: Delegator, + shares: T::Balance, + stake: T::Balance, + }, + /// Delegator unstaked towards a candidate with AutoCompounding Shares. + UnstakedAutoCompounding { + candidate: Candidate, + delegator: Delegator, + shares: T::Balance, + stake: T::Balance, + }, + /// Delegator staked towards a candidate for ManualRewards Shares. + StakedManualRewards { + candidate: Candidate, + delegator: Delegator, + shares: T::Balance, + stake: T::Balance, + }, + /// Delegator unstaked towards a candidate with ManualRewards Shares. + UnstakedManualRewards { + candidate: Candidate, + delegator: Delegator, + shares: T::Balance, + stake: T::Balance, + }, + /// Collator has been rewarded. + RewardedCollator { + collator: Candidate, + auto_compounding_rewards: T::Balance, + manual_claim_rewards: T::Balance, + }, + /// Delegators have been rewarded. + RewardedDelegators { + collator: Candidate, + auto_compounding_rewards: T::Balance, + manual_claim_rewards: T::Balance, + }, + /// Rewards manually claimed. + ClaimedManualRewards { + candidate: Candidate, + delegator: Delegator, + rewards: T::Balance, + }, + /// Swapped between AutoCompounding and ManualReward shares + SwappedPool { + candidate: Candidate, + delegator: Delegator, + source_pool: TargetPool, + source_shares: T::Balance, + source_stake: T::Balance, + target_shares: T::Balance, + target_stake: T::Balance, + pending_leaving: T::Balance, + released: T::Balance, + }, + } + + #[pallet::error] + pub enum Error { + InvalidPalletSetting, + DisabledFeature, + NoOneIsStaking, + StakeMustBeNonZero, + RewardsMustBeNonZero, + MathUnderflow, + MathOverflow, + NotEnoughShares, + TryingToLeaveTooSoon, + InconsistentState, + UnsufficientSharesForTransfer, + CandidateTransferingOwnSharesForbidden, + RequestCannotBeExecuted(u16), + SwapResultsInZeroShares, + } + + impl From for Error { + fn from(_: tp_maths::OverflowError) -> Self { + Error::MathOverflow + } + } + + impl From for Error { + fn from(_: tp_maths::UnderflowError) -> Self { + Error::MathUnderflow + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + use sp_std::collections::btree_set::BTreeSet; + let mut all_candidates = BTreeSet::new(); + for (candidate, _k2) in Pools::::iter_keys() { + all_candidates.insert(candidate); + } + + for candidate in all_candidates { + pools::check_candidate_consistency::(&candidate)?; + } + + // Sorted storage items are sorted + fn assert_is_sorted_and_unique(x: &[T], name: &str) { + assert!( + x.windows(2).all(|w| w[0] < w[1]), + "sorted list not sorted or not unique: {}", + name, + ); + } + assert_is_sorted_and_unique( + &SortedEligibleCandidates::::get(), + "SortedEligibleCandidates", + ); + + Ok(()) + } + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::rebalance_hold())] + pub fn rebalance_hold( + origin: OriginFor, + candidate: Candidate, + delegator: Delegator, + pool: AllTargetPool, + ) -> DispatchResultWithPostInfo { + // We don't care about the sender. + let _ = ensure_signed(origin)?; + + Calls::::rebalance_hold(candidate, delegator, pool) + } + + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::request_delegate())] + pub fn request_delegate( + origin: OriginFor, + candidate: Candidate, + pool: TargetPool, + stake: T::Balance, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + + Calls::::request_delegate(candidate, delegator, pool, stake) + } + + /// Execute pending operations can incur in claim manual rewards per operation, we simply add the worst case + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::execute_pending_operations(operations.len() as u32).saturating_add(T::WeightInfo::claim_manual_rewards(operations.len() as u32)))] + pub fn execute_pending_operations( + origin: OriginFor, + operations: Vec>, + ) -> DispatchResultWithPostInfo { + // We don't care about the sender. + let _ = ensure_signed(origin)?; + + Calls::::execute_pending_operations(operations) + } + + /// Request undelegate can incur in either claim manual rewards or hold rebalances, we simply add the worst case + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::request_undelegate().saturating_add(T::WeightInfo::claim_manual_rewards(1).max(T::WeightInfo::rebalance_hold())))] + pub fn request_undelegate( + origin: OriginFor, + candidate: Candidate, + pool: TargetPool, + amount: SharesOrStake, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + + Calls::::request_undelegate(candidate, delegator, pool, amount) + } + + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::claim_manual_rewards(pairs.len() as u32))] + pub fn claim_manual_rewards( + origin: OriginFor, + pairs: Vec<(Candidate, Delegator)>, + ) -> DispatchResultWithPostInfo { + // We don't care about the sender. + let _ = ensure_signed(origin)?; + + Calls::::claim_manual_rewards(&pairs) + } + + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::update_candidate_position(candidates.len() as u32))] + pub fn update_candidate_position( + origin: OriginFor, + candidates: Vec>, + ) -> DispatchResultWithPostInfo { + // We don't care about the sender. + let _ = ensure_signed(origin)?; + + Calls::::update_candidate_position(&candidates) + } + + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::swap_pool())] + pub fn swap_pool( + origin: OriginFor, + candidate: Candidate, + source_pool: TargetPool, + amount: SharesOrStake, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + + Calls::::swap_pool(candidate, delegator, source_pool, amount) + } + } + + impl Pallet { + pub fn computed_stake( + candidate: Candidate, + delegator: Delegator, + pool: AllTargetPool, + ) -> Option { + use pools::Pool; + match pool { + AllTargetPool::Joining => { + pools::Joining::::computed_stake(&candidate, &delegator) + } + AllTargetPool::AutoCompounding => { + pools::AutoCompounding::::computed_stake(&candidate, &delegator) + } + AllTargetPool::ManualRewards => { + pools::ManualRewards::::computed_stake(&candidate, &delegator) + } + AllTargetPool::Leaving => { + pools::Leaving::::computed_stake(&candidate, &delegator) + } + } + .ok() + .map(|x| x.0) + } + } + + impl tp_traits::DistributeRewards, CreditOf> for Pallet { + fn distribute_rewards( + candidate: Candidate, + rewards: CreditOf, + ) -> DispatchResultWithPostInfo { + pools::distribute_rewards::(&candidate, rewards) + } + } +} diff --git a/pallets/pooled-staking/src/mock.rs b/pallets/pooled-staking/src/mock.rs new file mode 100644 index 0000000..0b73525 --- /dev/null +++ b/pallets/pooled-staking/src/mock.rs @@ -0,0 +1,567 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + self as pallet_pooled_staking, + candidate::Candidates, + pools::Pool, + traits::{BlockNumberTimer, Timer}, + Candidate, Delegator, PendingOperationKey, PendingOperationKeyOf, TargetPool, + }, + frame_support::{ + parameter_types, + traits::{ + tokens::fungible::{Inspect, InspectHold}, + Everything, OnFinalize, OnInitialize, + }, + }, + frame_system::pallet_prelude::BlockNumberFor, + num_traits::Num, + parity_scale_codec::{Decode, Encode, MaxEncodedLen}, + scale_info::TypeInfo, + sp_core::{ConstU32, ConstU64, RuntimeDebug, H256}, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, Perbill, + }, +}; + +#[derive( + RuntimeDebug, + PartialEq, + Eq, + Encode, + Decode, + Copy, + Clone, + TypeInfo, + PartialOrd, + Ord, + MaxEncodedLen, +)] +pub enum HoldIdentifier { + Staking, +} + +type Block = frame_system::mocking::MockBlock; +pub type AccountId = u64; +pub type Balance = u128; + +pub const ACCOUNT_STAKING: u64 = 0; +pub const ACCOUNT_CANDIDATE_1: u64 = 1; +pub const ACCOUNT_CANDIDATE_2: u64 = 2; +pub const ACCOUNT_DELEGATOR_1: u64 = 3; +pub const ACCOUNT_DELEGATOR_2: u64 = 4; + +pub const KILO: u128 = 1000; +pub const MEGA: u128 = 1000 * KILO; +pub const GIGA: u128 = 1000 * MEGA; +pub const TERA: u128 = 1000 * GIGA; +pub const PETA: u128 = 1000 * TERA; +pub const DEFAULT_BALANCE: u128 = PETA; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Runtime + { + System: frame_system, + Balances: pallet_balances, + Staking: pallet_pooled_staking, + } +); + +impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; + type RuntimeTask = (); +} + +/// Allows to change ED mid-test. +pub struct MockExistentialDeposit; +impl MockExistentialDeposit { + pub fn get() -> Balance { + frame_support::storage::unhashed::get(b":mock_ed").unwrap_or(1) + } + + pub fn set(amount: Balance) { + frame_support::storage::unhashed::put(b":mock_ed", &amount); + } +} + +parameter_types! { + pub ExistentialDeposit: u128 = MockExistentialDeposit::get(); +} + +impl pallet_balances::Config for Runtime { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxHolds = ConstU32<5>; + type WeightInfo = (); +} + +pub const SHARE_INIT: u128 = MEGA; +pub const BLOCKS_TO_WAIT: u64 = 2; + +parameter_types! { + pub const StakingAccount: u64 = ACCOUNT_STAKING; + pub const InitialManualClaimShareValue: u128 = SHARE_INIT; + pub const InitialAutoCompoundingShareValue: u128 = SHARE_INIT; + pub const MinimumSelfDelegation: u128 = 10 * MEGA; + pub const RewardsCollatorCommission: Perbill = Perbill::from_percent(20); + pub const BlocksToWait: u64 = BLOCKS_TO_WAIT; +} + +impl pallet_pooled_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Balance = Balance; + type StakingAccount = StakingAccount; + type InitialManualClaimShareValue = InitialManualClaimShareValue; + type InitialAutoCompoundingShareValue = InitialAutoCompoundingShareValue; + type MinimumSelfDelegation = MinimumSelfDelegation; + type RewardsCollatorCommission = RewardsCollatorCommission; + type JoiningRequestTimer = BlockNumberTimer; + type LeavingRequestTimer = BlockNumberTimer; + // low value so we can test vec bounding, in practice it should be bigger + type EligibleCandidatesBufferSize = ConstU32<3>; + type EligibleCandidatesFilter = (); + type WeightInfo = (); + type RuntimeHoldReason = RuntimeHoldReason; +} + +pub trait PoolExt: Pool { + type OppositePool: PoolExt; + + fn target_pool() -> TargetPool; + fn event_staked( + candidate: Candidate, + delegator: Delegator, + shares: T::Balance, + stake: T::Balance, + ) -> crate::Event; + fn joining_operation_key( + candidate: Candidate, + at: ::Instant, + ) -> PendingOperationKeyOf; +} + +impl PoolExt for crate::pools::ManualRewards { + type OppositePool = crate::pools::AutoCompounding; + + fn target_pool() -> TargetPool { + TargetPool::ManualRewards + } + + fn event_staked( + candidate: Candidate, + delegator: Delegator, + shares: T::Balance, + stake: T::Balance, + ) -> crate::Event { + crate::Event::StakedManualRewards { + candidate, + delegator, + shares, + stake, + } + } + + fn joining_operation_key( + candidate: Candidate, + at: ::Instant, + ) -> PendingOperationKeyOf { + PendingOperationKey::JoiningManualRewards { candidate, at } + } +} + +impl PoolExt for crate::pools::AutoCompounding { + type OppositePool = crate::pools::ManualRewards; + + fn target_pool() -> TargetPool { + TargetPool::AutoCompounding + } + fn event_staked( + candidate: Candidate, + delegator: Delegator, + shares: T::Balance, + stake: T::Balance, + ) -> crate::Event { + crate::Event::StakedAutoCompounding { + candidate, + delegator, + shares, + stake, + } + } + + fn joining_operation_key( + candidate: Candidate, + at: ::Instant, + ) -> PendingOperationKeyOf { + PendingOperationKey::JoiningAutoCompounding { candidate, at } + } +} + +#[macro_export] +macro_rules! pool_test { + (fn $name:ident<$pool:ident>() { $body:expr }) => { + mod $name { + use super::*; + fn generic<$pool: PoolExt>() { + $body + } + + #[test] + fn manual() { + generic::>(); + } + + #[test] + fn auto() { + generic::>(); + } + } + }; +} + +pub fn total_balance(who: &AccountId) -> Balance { + Balances::total_balance(who) +} + +pub fn balance_hold(who: &AccountId) -> Balance { + Balances::balance_on_hold(&crate::HoldReason::PooledStake.into(), who) +} + +pub fn block_number() -> BlockNumberFor { + System::block_number() +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct State { + pub delegator_balance: Balance, + pub delegator_hold: Balance, + pub staking_balance: Balance, + pub candidate_total_stake: Balance, +} + +impl State { + pub fn extract(candidate: AccountId, delegator: AccountId) -> Self { + Self { + delegator_balance: total_balance(&delegator), + delegator_hold: balance_hold(&delegator), + staking_balance: total_balance(&ACCOUNT_STAKING), + candidate_total_stake: Candidates::::total_stake(&candidate).0, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct PoolState { + pub hold: Balance, + pub stake: Balance, +} + +impl PoolState { + pub fn extract>(candidate: AccountId, delegator: AccountId) -> Self { + Self { + hold: P::hold(&candidate, &delegator).0, + stake: P::computed_stake(&candidate, &delegator) + .expect("invalid state") + .0, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SignedBalance { + Positive(Balance), + Negative(Balance), +} + +#[allow(dead_code)] +pub fn round_down(value: T, increment: T) -> T { + if (value % increment).is_zero() { + value + } else { + (value / increment) * increment + } +} + +pub(crate) struct ExtBuilder { + // endowed accounts with balances + balances: Vec<(AccountId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { + balances: vec![ + (ACCOUNT_STAKING, 1 * DEFAULT_BALANCE), + (ACCOUNT_CANDIDATE_1, 1 * DEFAULT_BALANCE), + (ACCOUNT_CANDIDATE_2, 1 * DEFAULT_BALANCE), + (ACCOUNT_DELEGATOR_1, 1 * DEFAULT_BALANCE), + (ACCOUNT_DELEGATOR_2, 1 * DEFAULT_BALANCE), + ], + } + } +} + +impl ExtBuilder { + #[allow(dead_code)] + pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub(crate) fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .expect("Frame system builds valid default genesis config"); + + pallet_balances::GenesisConfig:: { + balances: self.balances, + } + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +/// Rolls forward one block. Returns the new block number. +#[allow(dead_code)] +pub(crate) fn roll_one_block() -> u64 { + // Staking::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + // Staking::on_initialize(System::block_number()); + System::block_number() +} + +/// Rolls to the desired block. Returns the number of blocks played. +#[allow(dead_code)] +pub(crate) fn roll_to(n: u64) -> u64 { + let mut num_blocks = 0; + let mut block = System::block_number(); + while block < n { + block = roll_one_block(); + num_blocks += 1; + } + num_blocks +} + +#[allow(dead_code)] +pub(crate) fn last_event() -> RuntimeEvent { + System::events().pop().expect("Event expected").event +} + +#[allow(dead_code)] +pub(crate) fn events() -> Vec> { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let RuntimeEvent::Staking(inner) = e { + Some(inner) + } else { + None + } + }) + .collect::>() +} + +/// Assert input equal to the last event emitted +#[macro_export] +macro_rules! assert_last_event { + ($event:expr) => { + match &$event { + e => assert_eq!(*e, $crate::mock::last_event()), + } + }; +} + +/// Compares the system events with passed in events +/// Prints highlighted diff iff assert_eq fails +#[macro_export] +macro_rules! assert_eq_events { + ($events:expr) => { + match &$events { + e => similar_asserts::assert_eq!(*e, $crate::mock::events()), + } + }; +} + +/// Compares the last N system events with passed in events, where N is the length of events passed +/// in. +/// +/// Prints highlighted diff iff assert_eq fails. +/// The last events from frame_system will be taken in order to match the number passed to this +/// macro. If there are insufficient events from frame_system, they will still be compared; the +/// output may or may not be helpful. +/// +/// Examples: +/// If frame_system has events [A, B, C, D, E] and events [C, D, E] are passed in, the result would +/// be a successful match ([C, D, E] == [C, D, E]). +/// +/// If frame_system has events [A, B, C, D] and events [B, C] are passed in, the result would be an +/// error and a hopefully-useful diff will be printed between [C, D] and [B, C]. +/// +/// Note that events are filtered to only match parachain-staking (see events()). +#[macro_export] +macro_rules! assert_eq_last_events { + ($events:expr) => { + $crate::assert_tail_eq!($events, $crate::mock::events()) + }; +} + +/// Assert that one array is equal to the tail of the other. A more generic and testable version of +/// assert_eq_last_events. +#[macro_export] +macro_rules! assert_tail_eq { + ($tail:expr, $arr:expr) => { + if $tail.len() != 0 { + // 0-length always passes + + if $tail.len() > $arr.len() { + similar_asserts::assert_eq!($tail, $arr); // will fail + } + + let len_diff = $arr.len() - $tail.len(); + similar_asserts::assert_eq!($tail, $arr[len_diff..]); + } + }; +} + +/// Panics if an event is not found in the system log of events +#[macro_export] +macro_rules! assert_event_emitted { + ($event:expr) => { + match &$event { + e => { + assert!( + $crate::mock::events().iter().find(|x| *x == e).is_some(), + "Event {:?} was not found in events: \n {:?}", + e, + $crate::mock::events() + ); + } + } + }; +} + +/// Panics if an event is found in the system log of events +#[macro_export] +macro_rules! assert_event_not_emitted { + ($event:expr) => { + match &$event { + e => { + assert!( + $crate::mock::events().iter().find(|x| *x == e).is_none(), + "Event {:?} was found in events: \n {:?}", + e, + $crate::mock::events() + ); + } + } + }; +} + +#[macro_export] +macro_rules! assert_fields_eq { + ($left:expr, $right:expr, $field:ident) => { + assert_eq!($left.$field, $right.$field); + }; + ($left:expr, $right:expr, [$( $field:ident ),+ $(,)?] ) => { + $( + assert_eq!($left.$field, $right.$field); + )+ + }; +} + +#[test] +fn assert_tail_eq_works() { + assert_tail_eq!(vec![1, 2], vec![0, 1, 2]); + + assert_tail_eq!(vec![1], vec![1]); + + assert_tail_eq!( + vec![0u32; 0], // 0 length array + vec![0u32; 1] // 1-length array + ); + + assert_tail_eq!(vec![0u32, 0], vec![0u32, 0]); +} + +#[test] +#[should_panic] +fn assert_tail_eq_panics_on_non_equal_tail() { + assert_tail_eq!(vec![2, 2], vec![0, 1, 2]); +} + +#[test] +#[should_panic] +fn assert_tail_eq_panics_on_empty_arr() { + assert_tail_eq!(vec![2, 2], vec![0u32; 0]); +} + +#[test] +#[should_panic] +fn assert_tail_eq_panics_on_longer_tail() { + assert_tail_eq!(vec![1, 2, 3], vec![1, 2]); +} + +#[test] +#[should_panic] +fn assert_tail_eq_panics_on_unequal_elements_same_length_array() { + assert_tail_eq!(vec![1, 2, 3], vec![0, 1, 2]); +} diff --git a/pallets/pooled-staking/src/pools.rs b/pallets/pooled-staking/src/pools.rs new file mode 100644 index 0000000..d230919 --- /dev/null +++ b/pallets/pooled-staking/src/pools.rs @@ -0,0 +1,564 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + candidate::Candidates, weights::WeightInfo, Candidate, Config, CreditOf, Delegator, Error, + Event, Pallet, Pools, PoolsKey, Shares, Stake, + }, + core::marker::PhantomData, + frame_support::{ + ensure, + pallet_prelude::*, + traits::{fungible::Balanced, Imbalance}, + }, + sp_core::Get, + sp_runtime::traits::{CheckedAdd, CheckedDiv, Zero}, + tp_maths::{ErrAdd, ErrMul, ErrSub, MulDiv}, +}; + +#[allow(dead_code)] +pub trait Pool { + /// Get the amount of shares a delegator have for given candidate. + fn shares(candidate: &Candidate, delegator: &Delegator) -> Shares; + /// Get the total amount of shares all delegators have for given candidate. + fn shares_supply(candidate: &Candidate) -> Shares; + /// Get the total amount of currency staked for given candidate / the value of all shares. + fn total_staked(candidate: &Candidate) -> Stake; + /// Get the amount of currency held for that pool in the delegator account. + fn hold(candidate: &Candidate, delegator: &Delegator) -> Stake; + + /// Set the amount of shares a delegator have for given candidate. + fn set_shares(candidate: &Candidate, delegator: &Delegator, value: Shares); + /// Set the total amount of shares all delegators have for given candidate. + fn set_shares_supply(candidate: &Candidate, value: Shares); + /// Set the total amount of currency staked for given candidate / the value of all shares. + fn set_total_staked(candidate: &Candidate, value: Stake); + /// Set the amount of currency held for that pool in the delegator account. + fn set_hold(candidate: &Candidate, delegator: &Delegator, value: Stake); + + /// Get the initial value of a share in case none exist yet. + fn initial_share_value() -> Stake; + + /// Convert an amount of shares to an amount of staked currency for given candidate. + /// Returns an error if there are no shares for that candidate. + fn shares_to_stake( + candidate: &Candidate, + shares: Shares, + ) -> Result, Error> { + let total_staked = Self::total_staked(candidate).0; + let supply = Self::shares_supply(candidate).0; + ensure!(!supply.is_zero(), Error::NoOneIsStaking); + + Ok(Stake(shares.0.mul_div(total_staked, supply)?)) + } + + /// Convert an amount of shares to an amount of staked currency for given candidate. + /// If this candidate have no shares then it uses `initial_share_value` to compute the value. + fn shares_to_stake_or_init( + candidate: &Candidate, + shares: Shares, + ) -> Result, Error> { + if Self::total_staked(candidate).0.is_zero() { + Ok(Stake(shares.0.err_mul(&Self::initial_share_value().0)?)) + } else { + Self::shares_to_stake(candidate, shares) + } + } + + /// Convert an amount of staked currency to an amount of shares for given candidate. + /// Returns an error if there are no shares for that candidate. + fn stake_to_shares( + candidate: &Candidate, + stake: Stake, + ) -> Result, Error> { + let total_staked = Self::total_staked(candidate).0; + let supply = Self::shares_supply(candidate).0; + ensure!(!supply.is_zero(), Error::NoOneIsStaking); + + Ok(Shares(stake.0.mul_div(supply, total_staked)?)) + } + + fn computed_stake( + candidate: &Candidate, + delegator: &Delegator, + ) -> Result, Error> { + let shares = Self::shares(candidate, delegator); + if shares.0.is_zero() { + return Ok(Stake(Zero::zero())); + } + + Self::shares_to_stake(candidate, shares) + } + + /// Convert an amount of staked currency to an amount of shares for given candidate. + /// If this candidate have no shares then it uses `initial_share_value` to compute the value. + fn stake_to_shares_or_init( + candidate: &Candidate, + stake: Stake, + ) -> Result, Error> { + if Self::total_staked(candidate).0.is_zero() { + Ok(Shares( + stake + .0 + .checked_div(&Self::initial_share_value().0) + .ok_or(Error::::InvalidPalletSetting)?, + )) + } else { + Self::stake_to_shares(candidate, stake) + } + } + + /// Increase the total stake of a pool without creating new shares, which basically increase + /// the value of each share. + fn share_stake_among_holders( + candidate: &Candidate, + stake: Stake, + ) -> Result<(), Error> { + let total_staked = Self::total_staked(candidate).0; + let total_staked = total_staked.err_add(&stake.0)?; + Self::set_total_staked(candidate, Stake(total_staked)); + Ok(()) + } + + /// Decrease the total stake of a pool without creating new shares, which basically decrease + /// the value of each share. + fn slash_stake_among_holders( + candidate: &Candidate, + stake: Stake, + ) -> Result<(), Error> { + let total_staked = Self::total_staked(candidate).0; + let total_staked = total_staked.err_sub(&stake.0)?; + Self::set_total_staked(candidate, Stake(total_staked)); + Ok(()) + } + + /// Add new shares for that delegator towards the given candidate. + /// Function returns the value of those new shares. + /// Returns an error if underflow/overflows occurs. + fn add_shares( + candidate: &Candidate, + delegator: &Delegator, + shares: Shares, + ) -> Result, Error> { + ensure!(!shares.0.is_zero(), Error::StakeMustBeNonZero); + + let stake = Self::shares_to_stake_or_init(candidate, shares)?; + + let new_shares_supply = Self::shares_supply(candidate).0.err_add(&shares.0)?; + let new_shares = Self::shares(candidate, delegator).0.err_add(&shares.0)?; + let new_total_stake = Self::total_staked(candidate).0.err_add(&stake.0)?; + + Self::set_shares_supply(candidate, Shares(new_shares_supply)); + Self::set_shares(candidate, delegator, Shares(new_shares)); + Self::set_total_staked(candidate, Stake(new_total_stake)); + + Ok(stake) + } + + /// Remove shares for that delegator towards the given candidate. + /// Function returns the value of those removed shares. + /// Returns an error if that delegator don't have enough shares or if underflow/overflows occurs. + fn sub_shares( + candidate: &Candidate, + delegator: &Delegator, + shares: Shares, + ) -> Result, Error> { + ensure!(!shares.0.is_zero(), Error::StakeMustBeNonZero); + + let stake = Self::shares_to_stake(candidate, shares)?; + + let new_shares_supply = Self::shares_supply(candidate).0.err_sub(&shares.0)?; + let new_shares = Self::shares(candidate, delegator).0.err_sub(&shares.0)?; + let new_total_stake = Self::total_staked(candidate).0.err_sub(&stake.0)?; + + Self::set_shares_supply(candidate, Shares(new_shares_supply)); + Self::set_shares(candidate, delegator, Shares(new_shares)); + Self::set_total_staked(candidate, Stake(new_total_stake)); + + Ok(stake) + } + + fn increase_hold( + candidate: &Candidate, + delegator: &Delegator, + stake: &Stake, + ) -> Result<(), Error> { + let hold = Self::hold(candidate, delegator); + let hold = hold.0.err_add(&stake.0)?; + Self::set_hold(candidate, delegator, Stake(hold)); + Ok(()) + } + + fn decrease_hold( + candidate: &Candidate, + delegator: &Delegator, + stake: &Stake, + ) -> Result<(), Error> { + let hold = Self::hold(candidate, delegator); + let hold = hold.0.err_sub(&stake.0)?; + Self::set_hold(candidate, delegator, Stake(hold)); + Ok(()) + } +} + +pub fn check_candidate_consistency(candidate: &Candidate) -> Result<(), Error> { + let total0 = Pools::::get(candidate, &PoolsKey::CandidateTotalStake); + + let joining = Joining::::total_staked(candidate).0; + let auto = AutoCompounding::::total_staked(candidate).0; + let manual = ManualRewards::::total_staked(candidate).0; + + let total1 = joining + .checked_add(&auto) + .ok_or(Error::InconsistentState)? + .checked_add(&manual) + .ok_or(Error::InconsistentState)?; + + ensure!(total0 == total1, Error::InconsistentState); + + Ok(()) +} + +macro_rules! impl_pool { + ($name:ident, $shares:ident, $supply:ident, $total:ident, $hold: ident, $init:expr $(,)?) => { + pub struct $name(PhantomData); + impl Pool for $name { + fn shares(candidate: &Candidate, delegator: &Delegator) -> Shares { + Shares(Pools::::get( + candidate, + &PoolsKey::$shares { + delegator: delegator.clone(), + }, + )) + } + + fn shares_supply(candidate: &Candidate) -> Shares { + Shares(Pools::::get(candidate, &PoolsKey::$supply)) + } + + fn total_staked(candidate: &Candidate) -> Stake { + Stake(Pools::::get(candidate, &PoolsKey::$total)) + } + + fn hold(candidate: &Candidate, delegator: &Delegator) -> Stake { + Stake(Pools::::get( + candidate, + &PoolsKey::$hold { + delegator: delegator.clone(), + }, + )) + } + + fn set_shares( + candidate: &Candidate, + delegator: &Delegator, + value: Shares, + ) { + Pools::::set( + candidate, + &PoolsKey::$shares { + delegator: delegator.clone(), + }, + value.0, + ) + } + + fn set_shares_supply(candidate: &Candidate, value: Shares) { + Pools::::set(candidate, &PoolsKey::$supply, value.0) + } + + fn set_total_staked(candidate: &Candidate, value: Stake) { + Pools::::set(candidate, &PoolsKey::$total, value.0) + } + + fn set_hold( + candidate: &Candidate, + delegator: &Delegator, + value: Stake, + ) { + Pools::::set( + candidate, + &PoolsKey::$hold { + delegator: delegator.clone(), + }, + value.0, + ) + } + + fn initial_share_value() -> Stake { + Stake($init) + } + } + }; +} + +impl_pool!( + Joining, + JoiningShares, + JoiningSharesSupply, + JoiningSharesTotalStaked, + JoiningSharesHeldStake, + if cfg!(test) { 2u32 } else { 1 }.into(), +); + +impl_pool!( + AutoCompounding, + AutoCompoundingShares, + AutoCompoundingSharesSupply, + AutoCompoundingSharesTotalStaked, + AutoCompoundingSharesHeldStake, + T::InitialAutoCompoundingShareValue::get(), +); + +impl_pool!( + ManualRewards, + ManualRewardsShares, + ManualRewardsSharesSupply, + ManualRewardsSharesTotalStaked, + ManualRewardsSharesHeldStake, + T::InitialManualClaimShareValue::get(), +); + +impl_pool!( + Leaving, + LeavingShares, + LeavingSharesSupply, + LeavingSharesTotalStaked, + LeavingSharesHeldStake, + if cfg!(test) { 3u32 } else { 1u32 }.into(), +); + +impl ManualRewards { + #[allow(dead_code)] + pub fn pending_rewards( + candidate: &Candidate, + delegator: &Delegator, + ) -> Result, Error> { + let shares = Self::shares(candidate, delegator); + + if shares.0.is_zero() { + return Ok(Stake(0u32.into())); + } + + let counter = Pools::::get(candidate, &PoolsKey::ManualRewardsCounter); + let checkpoint = Pools::::get( + candidate, + &PoolsKey::ManualRewardsCheckpoint { + delegator: delegator.clone(), + }, + ); + + // TODO: Should be safe to wrap around. + let diff = counter.err_sub(&checkpoint)?; + Ok(Stake(diff.err_mul(&shares.0)?)) + } + + /// Increase the rewards of the ManualRewards pool with best effort. + /// Returns the actual amount distributed (after rounding). + pub fn increase_rewards( + candidate: &Candidate, + rewards: Stake, + ) -> Result, Error> { + let Shares(supply) = Self::shares_supply(candidate); + if supply.is_zero() { + return Ok(Stake(Zero::zero())); + } + + let rewards_per_share = rewards + .0 + .checked_div(&supply) + .ok_or(Error::::InconsistentState)?; + if rewards_per_share.is_zero() { + return Ok(Stake(Zero::zero())); + } + + let rewards = rewards_per_share.err_mul(&supply)?; + + let counter = Pools::::get(candidate, &PoolsKey::ManualRewardsCounter); + let counter = counter.err_add(&rewards_per_share)?; + Pools::::set(candidate, &PoolsKey::ManualRewardsCounter, counter); + + Ok(Stake(rewards)) + } + + pub fn claim_rewards( + candidate: &Candidate, + delegator: &Delegator, + ) -> Result, Error> { + let shares = Self::shares(candidate, delegator); + + let counter = Pools::::get(candidate, &PoolsKey::ManualRewardsCounter); + let checkpoint = Pools::::get( + candidate, + &PoolsKey::ManualRewardsCheckpoint { + delegator: delegator.clone(), + }, + ); + + // TODO: Should be safe to wrap around. + let diff = counter.err_sub(&checkpoint)?; + + if diff.is_zero() { + return Ok(Stake(0u32.into())); + } + + let rewards = diff.err_mul(&shares.0)?; + + // Update checkpoint + Pools::::set( + candidate, + &PoolsKey::ManualRewardsCheckpoint { + delegator: delegator.clone(), + }, + counter, + ); + + Ok(Stake(rewards)) + } +} + +/// Perform rewards distribution for the provided candidate. +/// +/// The pallet considered that it already posses the rewards in its account, +/// and it is the responsibility of the caller to transfer or mint the currency +/// to the staking pallet account. +/// +/// Rewards are split using `RewardsCollatorCommission` between the candidate +/// and all the delegators (including the candidate self-delegation). For each, +/// the rewards are then split according to the value of all the ManualRewards +/// and AutoCompounding shares. +/// +/// As candidate rewards will give increase the candidate auto compounding +/// self-delegation, the delegator rewards are distributed first. ManualRewards +/// pool rewards are first distributed by increasing the pool counter, which may +/// result in some rounding. As distributing the AutoCompounding pool rewards +/// consists of simply increasing `AutoCompoundingSharesTotalStaked`, it is not +/// subject to rounding and it can absorb the rounding dust from ManualRewards +/// reward distribution. +/// +/// Then it is time to distribute the candidate dedicated rewards. For +/// AutoCompounding, it is as if the candidate received the rewards then +/// self-delegated (instantly). It is thus implemented by creating new +/// AutoCompounding shares. This can lead to some rounding, which will be +/// absorbed in the ManualRewards distribution, which simply consist of +/// transfering the funds to the candidate account. +#[frame_support::transactional] +pub fn distribute_rewards( + candidate: &Candidate, + rewards: CreditOf, +) -> DispatchResultWithPostInfo { + let candidate_manual_rewards = distribute_rewards_inner::(candidate, rewards.peek())?; + + let (candidate_manual_rewards, other_rewards) = rewards.split(candidate_manual_rewards); + + if !candidate_manual_rewards.peek().is_zero() { + T::Currency::resolve(candidate, candidate_manual_rewards) + .map_err(|_| DispatchError::NoProviders)?; + } + + T::Currency::resolve(&T::StakingAccount::get(), other_rewards) + .map_err(|_| DispatchError::NoProviders)?; + + Ok(Some(T::WeightInfo::distribute_rewards()).into()) +} + +fn distribute_rewards_inner( + candidate: &Candidate, + rewards: T::Balance, +) -> Result> { + // `RewardsCollatorCommission` is a `Perbill` so we're not worried about overflow. + let candidate_rewards = T::RewardsCollatorCommission::get() * rewards; + let delegators_rewards = rewards.err_sub(&candidate_rewards)?; + + let Stake(auto_total_stake) = AutoCompounding::::total_staked(candidate); + let Stake(manual_total_stake) = ManualRewards::::total_staked(candidate); + let combined_total_stake = auto_total_stake.err_add(&manual_total_stake)?; + + let candidate_manual_stake = if manual_total_stake.is_zero() { + Zero::zero() + } else { + ManualRewards::::computed_stake(candidate, candidate)?.0 + }; + + // Distribute delegators ManualRewards rewards, it implies some rounding. + let delegators_manual_rewards = if manual_total_stake.is_zero() { + Zero::zero() + } else { + let rewards = delegators_rewards.mul_div(manual_total_stake, combined_total_stake)?; + ManualRewards::::increase_rewards(candidate, Stake(rewards))?.0 + }; + + // Distribute delegators AutoCompounding rewards with dust from ManualRewards. + // If there is no auto compounding stake but auto compounding rewards it + // means it comes from manual rewards rounding. Having non-zero total stake + // with 0 share supply will cause issues, so in this case we distribute this + // dust as candidate manual rewards. + let delegators_auto_rewards = delegators_rewards.err_sub(&delegators_manual_rewards)?; + let (delegators_auto_rewards, delegators_auto_dust) = if !auto_total_stake.is_zero() { + AutoCompounding::::share_stake_among_holders(candidate, Stake(delegators_auto_rewards))?; + (delegators_auto_rewards, Zero::zero()) + } else { + (Zero::zero(), delegators_auto_rewards) + }; + + // Distribute candidate AutoCompounding rewards, it implies some rounding. + let candidate_auto_rewards = if auto_total_stake.is_zero() { + Zero::zero() + } else { + 'a: { + let candidate_auto_stake = + AutoCompounding::::computed_stake(candidate, candidate)?.0; + let candidate_combined_stake = candidate_manual_stake.err_add(&candidate_auto_stake)?; + + if candidate_combined_stake.is_zero() { + break 'a Zero::zero(); + } + + let rewards = + candidate_rewards.mul_div(candidate_auto_stake, candidate_combined_stake)?; + let new_shares = AutoCompounding::::stake_to_shares(candidate, Stake(rewards))?; + + if new_shares.0.is_zero() { + break 'a Zero::zero(); + } + + AutoCompounding::::add_shares(candidate, candidate, new_shares)?.0 + } + }; + + // Distribute candidate ManualRewards rewards with dust from AutoCompounding. + // The amount is returned by the function and will be transfered to the candidate account. + let candidate_manual_rewards = candidate_rewards + .err_sub(&candidate_auto_rewards)? + .err_add(&delegators_auto_dust)?; + + let additional_stake = delegators_auto_rewards.err_add(&candidate_auto_rewards)?; + Candidates::::add_total_stake(candidate, &Stake(additional_stake))?; + + Pallet::::deposit_event(Event::::RewardedCollator { + collator: candidate.clone(), + auto_compounding_rewards: candidate_auto_rewards, + manual_claim_rewards: candidate_manual_rewards, + }); + Pallet::::deposit_event(Event::::RewardedDelegators { + collator: candidate.clone(), + auto_compounding_rewards: delegators_auto_rewards, + manual_claim_rewards: delegators_manual_rewards, + }); + + Ok(candidate_manual_rewards) +} diff --git a/pallets/pooled-staking/src/tests/candidates.rs b/pallets/pooled-staking/src/tests/candidates.rs new file mode 100644 index 0000000..528cb48 --- /dev/null +++ b/pallets/pooled-staking/src/tests/candidates.rs @@ -0,0 +1,510 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use crate::{assert_eq_last_events, candidate::EligibleCandidate, SortedEligibleCandidates}; + +use super::*; + +pool_test!( + fn self_delegation_below_minimum

() { + ExtBuilder::default().build().execute_with(|| { + let requested_amount = MinimumSelfDelegation::get() - 1; + let final_amount = round_down( + requested_amount, + P::shares_to_stake_or_init(&ACCOUNT_CANDIDATE_1, Shares(1)) + .unwrap() + .0, + ); + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + request_amount: requested_amount, + expected_increase: final_amount, + ..default() + } + .test::

(); + + assert_eq_events!(vec![ + Event::IncreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: round_down(requested_amount, 2), + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: round_down(requested_amount, 2), + self_delegation: round_down(requested_amount, 2), + before: None, + after: None, + }, + Event::RequestedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: P::target_pool(), + pending: round_down(requested_amount, 2), + }, + Event::DecreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: round_down(requested_amount, 2) - final_amount, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: final_amount, + self_delegation: final_amount, + before: None, + after: None, + }, + P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_CANDIDATE_1, 9, final_amount), + Event::ExecutedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: P::target_pool(), + staked: final_amount, + released: round_down(requested_amount, 2) - final_amount, + }, + ]); + }) + } +); + +pool_test!( + fn self_delegation_above_minimum

() { + ExtBuilder::default().build().execute_with(|| { + let requested_amount = MinimumSelfDelegation::get(); + let final_amount = round_down( + requested_amount, + P::shares_to_stake_or_init(&ACCOUNT_CANDIDATE_1, Shares(1)) + .unwrap() + .0, + ); + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + request_amount: requested_amount, + expected_increase: final_amount, + ..default() + } + .test::

(); + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + request_amount: requested_amount, + expected_increase: final_amount, + ..default() + } + .test::

(); + + FullUndelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + request_amount: SharesOrStake::Stake(requested_amount * 2), + expected_removed: requested_amount * 2, + expected_leaving: round_down(requested_amount * 2, 3), + ..default() + } + .test::

(); + + assert_eq_events!(vec![ + // delegation 1 + Event::IncreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: round_down(requested_amount, 2), + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: round_down(requested_amount, 2), + self_delegation: round_down(requested_amount, 2), + before: None, + after: Some(0), + }, + Event::RequestedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: P::target_pool(), + pending: round_down(requested_amount, 2), + }, + P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_CANDIDATE_1, 10, final_amount), + Event::ExecutedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: P::target_pool(), + staked: final_amount, + released: round_down(requested_amount, 2) - final_amount, + }, + // delegation 2 + Event::IncreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: round_down(requested_amount, 2), + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: round_down(requested_amount * 2, 2), + self_delegation: round_down(requested_amount * 2, 2), + before: Some(0), + after: Some(0), + }, + Event::RequestedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: P::target_pool(), + pending: round_down(requested_amount, 2), + }, + P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_CANDIDATE_1, 10, final_amount), + Event::ExecutedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: P::target_pool(), + staked: final_amount, + released: round_down(requested_amount, 2) - final_amount, + }, + // undelegation + Event::DecreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: requested_amount * 2, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: 0, + self_delegation: 0, + before: Some(0), + after: None, + }, + Event::RequestedUndelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + from: P::target_pool(), + pending: round_down(requested_amount * 2, 3), + released: 2, + }, + Event::ExecutedUndelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + released: round_down(requested_amount * 2, 3) + } + ]); + }) + } +); + +#[test] +fn many_candidates_mixed_pools() { + ExtBuilder::default().build().execute_with(|| { + let share = InitialAutoCompoundingShareValue::get(); + // for simplicity we consider shares of both pools have the same value. + assert_eq!( + InitialAutoCompoundingShareValue::get(), + InitialManualClaimShareValue::get() + ); + + struct Action { + candidate: AccountId, + delegator: AccountId, + join: bool, + auto: bool, + amount: Balance, + total_stake: Balance, + total_self: Balance, + + rank_before: Option, + rank_after: Option, + } + + fn perform_actions(actions: &[Action]) { + let share = InitialAutoCompoundingShareValue::get(); + for action in actions { + match action { + Action { + join: true, + auto: true, + .. + } => { + FullDelegation { + candidate: action.candidate, + delegator: action.delegator, + request_amount: action.amount, + expected_increase: action.amount, + ..default() + } + .test::>(); + + assert_eq_last_events!(vec![ + Event::::IncreasedStake { + candidate: action.candidate, + stake_diff: action.amount, + }, + Event::UpdatedCandidatePosition { + candidate: action.candidate, + stake: action.total_stake, + self_delegation: action.total_self, + before: action.rank_before, + after: action.rank_after, + }, + Event::RequestedDelegate { + candidate: action.candidate, + delegator: action.delegator, + pool: TargetPool::AutoCompounding, + pending: action.amount, + }, + Event::StakedAutoCompounding { + candidate: action.candidate, + delegator: action.delegator, + shares: action.amount / share, + stake: action.amount, + }, + Event::ExecutedDelegate { + candidate: action.candidate, + delegator: action.delegator, + pool: TargetPool::AutoCompounding, + staked: action.amount, + released: 0, + }, + ]) + } + Action { + join: true, + auto: false, + .. + } => { + FullDelegation { + candidate: action.candidate, + delegator: action.delegator, + request_amount: action.amount, + expected_increase: action.amount, + ..default() + } + .test::>(); + + assert_eq_last_events!(vec![ + Event::::IncreasedStake { + candidate: action.candidate, + stake_diff: action.amount, + }, + Event::UpdatedCandidatePosition { + candidate: action.candidate, + stake: action.total_stake, + self_delegation: action.total_self, + before: action.rank_before, + after: action.rank_after, + }, + Event::RequestedDelegate { + candidate: action.candidate, + delegator: action.delegator, + pool: TargetPool::ManualRewards, + pending: action.amount, + }, + Event::StakedManualRewards { + candidate: action.candidate, + delegator: action.delegator, + shares: action.amount / share, + stake: action.amount, + }, + Event::ExecutedDelegate { + candidate: action.candidate, + delegator: action.delegator, + pool: TargetPool::ManualRewards, + staked: action.amount, + released: 0, + }, + ]) + } + Action { + join: false, + auto: true, + .. + } => { + FullUndelegation { + candidate: action.candidate, + delegator: action.delegator, + request_amount: SharesOrStake::Stake(action.amount), + expected_removed: action.amount, + expected_leaving: round_down(action.amount, 3), + ..default() + } + .test::>(); + + assert_eq_last_events!(vec![ + Event::::DecreasedStake { + candidate: action.candidate, + stake_diff: action.amount, + }, + Event::UpdatedCandidatePosition { + candidate: action.candidate, + stake: action.total_stake, + self_delegation: action.total_self, + before: action.rank_before, + after: action.rank_after, + }, + Event::RequestedUndelegate { + candidate: action.candidate, + delegator: action.delegator, + from: TargetPool::AutoCompounding, + pending: round_down(action.amount, 3), + released: action.amount - round_down(action.amount, 3), + }, + Event::ExecutedUndelegate { + candidate: action.candidate, + delegator: action.delegator, + released: round_down(action.amount, 3), + }, + ]) + } + _ => todo!(), + } + } + } + + perform_actions(&[ + Action { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + join: true, + auto: true, + amount: share * 11, + total_stake: share * 11, + total_self: share * 11, + rank_before: None, + rank_after: Some(0), + }, + Action { + candidate: ACCOUNT_CANDIDATE_2, + delegator: ACCOUNT_CANDIDATE_2, + join: true, + auto: false, + amount: share * 10, + total_stake: share * 10, + total_self: share * 10, + rank_before: None, + rank_after: Some(1), + }, + Action { + candidate: ACCOUNT_CANDIDATE_2, + delegator: ACCOUNT_DELEGATOR_1, + join: true, + auto: true, + amount: share * 3, + total_stake: share * 13, + total_self: share * 10, + rank_before: Some(1), + rank_after: Some(0), + }, + Action { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_2, + join: true, + auto: false, + amount: share, + total_stake: share * 12, + total_self: share * 11, + rank_before: Some(1), + rank_after: Some(1), + }, + Action { + candidate: ACCOUNT_DELEGATOR_1, + delegator: ACCOUNT_DELEGATOR_1, + join: true, + auto: true, + amount: share * 11, + total_stake: share * 11, + total_self: share * 11, + rank_before: None, + rank_after: Some(2), + }, + Action { + candidate: ACCOUNT_DELEGATOR_2, + delegator: ACCOUNT_DELEGATOR_2, + join: true, + auto: true, + amount: share * 10, + total_stake: share * 10, + total_self: share * 10, + rank_before: None, + rank_after: None, // list is full + }, + ]); + + assert_eq!( + SortedEligibleCandidates::::get().into_inner(), + vec![ + EligibleCandidate { + candidate: ACCOUNT_CANDIDATE_2, + stake: share * 13, + }, + EligibleCandidate { + candidate: ACCOUNT_CANDIDATE_1, + stake: share * 12, + }, + EligibleCandidate { + candidate: ACCOUNT_DELEGATOR_1, + stake: share * 11, + }, + ] + ); + + // We make candidate 1 leave, which doesn't make the out of list + // candidate back in the list. + perform_actions(&[Action { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + join: false, + auto: true, + amount: share * 11, + total_stake: share * 1, + total_self: 0, + rank_before: Some(1), + rank_after: None, + }]); + + assert_eq!( + SortedEligibleCandidates::::get().into_inner(), + vec![ + EligibleCandidate { + candidate: ACCOUNT_CANDIDATE_2, + stake: share * 13, + }, + EligibleCandidate { + candidate: ACCOUNT_DELEGATOR_1, + stake: share * 11, + }, + ] + ); + + // It needs to be done manually. + assert_ok!(Staking::update_candidate_position( + RuntimeOrigin::signed(ACCOUNT_DELEGATOR_2), + vec![ACCOUNT_DELEGATOR_2] + )); + + assert_eq!( + SortedEligibleCandidates::::get().into_inner(), + vec![ + EligibleCandidate { + candidate: ACCOUNT_CANDIDATE_2, + stake: share * 13, + }, + EligibleCandidate { + candidate: ACCOUNT_DELEGATOR_1, + stake: share * 11, + }, + EligibleCandidate { + candidate: ACCOUNT_DELEGATOR_2, + stake: share * 10, + }, + ] + ); + }) +} diff --git a/pallets/pooled-staking/src/tests/delegator_flow.rs b/pallets/pooled-staking/src/tests/delegator_flow.rs new file mode 100644 index 0000000..fe25630 --- /dev/null +++ b/pallets/pooled-staking/src/tests/delegator_flow.rs @@ -0,0 +1,571 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use {super::*, crate::assert_eq_last_events}; + +pool_test!( + fn empty_delegation

() { + ExtBuilder::default().build().execute_with(|| { + let before = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + let pool_before = + PoolState::extract::(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + + assert_noop!( + Staking::request_delegate( + RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), + ACCOUNT_CANDIDATE_1, + P::target_pool(), + 0 + ), + Error::::StakeMustBeNonZero + ); + + let after = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + let pool_after = + PoolState::extract::(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + + assert_eq!(before, after); + assert_eq!(pool_before, pool_after); + + assert_eq_events!(Vec::>::new()); + }) + } +); + +pool_test!( + fn delegation_request

() { + ExtBuilder::default().build().execute_with(|| { + let amount = 3324; + RequestDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + amount: amount + 1, // to test joining rounding + expected_joining: amount, + } + .test(); + + assert_eq_events!(vec![ + Event::IncreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: amount, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: amount, + self_delegation: 0, + before: None, + after: None, + }, + Event::RequestedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + pending: amount + }, + ]); + }) + } +); + +pool_test!( + fn delegation_request_more_than_available

() { + ExtBuilder::default().build().execute_with(|| { + let amount = DEFAULT_BALANCE; // not enough to keep ED + + let before = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + let pool_before = + PoolState::extract::(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + + assert_noop!( + Staking::request_delegate( + RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), + ACCOUNT_CANDIDATE_1, + P::target_pool(), + amount, + ), + TokenError::FundsUnavailable + ); + + let after = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + let pool_after = + PoolState::extract::(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1); + + assert_eq!(before, after); + assert_eq!(pool_before, pool_after); + + assert_eq_events!(Vec::>::new()); + }) + } +); + +pool_test!( + fn delegation_execution

() { + ExtBuilder::default().build().execute_with(|| { + let final_amount = 2 * SHARE_INIT; + let requested_amount = final_amount + 10; // test share rounding + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: requested_amount, + expected_increase: final_amount, + ..default() + } + .test::

(); + + assert_eq_events!(vec![ + Event::IncreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: requested_amount, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: requested_amount, + self_delegation: 0, + before: None, + after: None, + }, + Event::RequestedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + pending: requested_amount, + }, + Event::DecreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: 10, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: final_amount, + self_delegation: 0, + before: None, + after: None, + }, + P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, final_amount), + Event::ExecutedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + staked: final_amount, + released: 10, + }, + ]); + }) + } +); + +pool_test!( + fn delegation_execution_too_soon

() { + ExtBuilder::default().build().execute_with(|| { + let final_amount = 2 * SHARE_INIT; + let block_number = block_number(); + + RequestDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + amount: final_amount, + expected_joining: final_amount, + } + .test(); + roll_to(block_number + BLOCKS_TO_WAIT - 1); // too soon + + assert_noop!( + Staking::execute_pending_operations( + RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), + vec![PendingOperationQuery { + delegator: ACCOUNT_DELEGATOR_1, + operation: P::joining_operation_key(ACCOUNT_CANDIDATE_1, block_number) + }] + ), + Error::::RequestCannotBeExecuted(0) + ); + }) + } +); + +pool_test!( + fn undelegation_execution_too_soon

() { + ExtBuilder::default().build().execute_with(|| { + let final_amount = 2 * SHARE_INIT; + let leaving_amount = round_down(final_amount, 3); // test leaving rounding + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: final_amount, + expected_increase: final_amount, + ..default() + } + .test::

(); + + let block_number = block_number(); + + RequestUndelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: SharesOrStake::Stake(final_amount), + expected_removed: final_amount, + expected_leaving: leaving_amount, + ..default() + } + .test::

(); + + roll_to(block_number + BLOCKS_TO_WAIT - 1); // too soon + assert_noop!( + Staking::execute_pending_operations( + RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), + vec![PendingOperationQuery { + delegator: ACCOUNT_DELEGATOR_1, + operation: PendingOperationKey::Leaving { + candidate: ACCOUNT_CANDIDATE_1, + at: block_number, + } + }] + ), + Error::::RequestCannotBeExecuted(0) + ); + }) + } +); + +pool_test!( + fn undelegation_execution

() { + ExtBuilder::default().build().execute_with(|| { + let final_amount = 2 * SHARE_INIT; + let requested_amount = final_amount + 10; // test share rounding + let leaving_amount = round_down(final_amount, 3); // test leaving rounding + + assert_eq!(leaving_amount, 1_999_998); + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: requested_amount, + expected_increase: final_amount, + ..default() + } + .test::

(); + + FullUndelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: SharesOrStake::Stake(final_amount), + expected_removed: final_amount, + expected_leaving: leaving_amount, + ..default() + } + .test::

(); + + assert_eq_events!(vec![ + // delegate request + Event::IncreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: requested_amount, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: requested_amount, + self_delegation: 0, + before: None, + after: None, + }, + Event::RequestedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + pending: requested_amount + }, + // delegate exec + Event::DecreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: 10, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: final_amount, + self_delegation: 0, + before: None, + after: None, + }, + P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, final_amount), + Event::ExecutedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + staked: final_amount, + released: 10, + }, + // undelegate request + Event::DecreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: final_amount, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: 0, + self_delegation: 0, + before: None, + after: None, + }, + Event::RequestedUndelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + from: P::target_pool(), + pending: leaving_amount, + released: 2 + }, + // undelegate exec + Event::ExecutedUndelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + released: leaving_amount, + }, + ]); + }) + } +); + +pool_test!( + fn undelegation_execution_amount_in_shares

() { + ExtBuilder::default().build().execute_with(|| { + let joining_amount = 2 * SHARE_INIT; + let joining_requested_amount = joining_amount + 10; // test share rounding + + let leaving_requested_amount = SHARE_INIT; + let leaving_amount = round_down(leaving_requested_amount, 3); // test leaving rounding + + assert_eq!(leaving_amount, 999_999); + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: joining_requested_amount, + expected_increase: joining_amount, + ..default() + } + .test::

(); + + FullUndelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: SharesOrStake::Shares(1), + expected_removed: leaving_requested_amount, + expected_leaving: leaving_amount, + ..default() + } + .test::

(); + + assert_eq_events!(vec![ + // delegate request + Event::IncreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: joining_requested_amount, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: joining_requested_amount, + self_delegation: 0, + before: None, + after: None, + }, + Event::RequestedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + pending: joining_requested_amount + }, + // delegate exec + Event::DecreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: 10, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: joining_amount, + self_delegation: 0, + before: None, + after: None, + }, + P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, joining_amount), + Event::ExecutedDelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: P::target_pool(), + staked: joining_amount, + released: 10, + }, + // undelegate request + Event::DecreasedStake { + candidate: ACCOUNT_CANDIDATE_1, + stake_diff: leaving_requested_amount, + }, + Event::UpdatedCandidatePosition { + candidate: ACCOUNT_CANDIDATE_1, + stake: joining_amount - leaving_requested_amount, + self_delegation: 0, + before: None, + after: None, + }, + Event::RequestedUndelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + from: P::target_pool(), + pending: leaving_amount, + released: 1 + }, + // undelegate exec + Event::ExecutedUndelegate { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + released: leaving_amount, + }, + ]); + }) + } +); + +pool_test!( + fn swap_works

() { + ExtBuilder::default().build().execute_with(|| { + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: 10 * SHARE_INIT, + expected_increase: 10 * SHARE_INIT, + ..default() + } + .test::

(); + + Swap { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + requested_amount: SharesOrStake::Stake(5 * SHARE_INIT + 10), + expected_removed: 5 * SHARE_INIT, + expected_restaked: 5 * SHARE_INIT, + ..default() + } + .test::

(); + + assert_eq_last_events!(vec![Event::::SwappedPool { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + source_pool: P::target_pool(), + source_shares: 5, + source_stake: 5 * SHARE_INIT, + target_shares: 5, + target_stake: 5 * SHARE_INIT, + pending_leaving: 0, + released: 0, + }]); + }) + } +); + +pool_test!( + fn swap_too_much

() { + ExtBuilder::default().build().execute_with(|| { + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: 10 * SHARE_INIT, + expected_increase: 10 * SHARE_INIT, + ..default() + } + .test::

(); + + assert_noop!( + Staking::swap_pool( + RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), + ACCOUNT_CANDIDATE_1, + P::target_pool(), + SharesOrStake::Shares(11), + ), + Error::::MathUnderflow + ); + }) + } +); + +pool_test!( + fn swap_with_rounding

() { + ExtBuilder::default().build().execute_with(|| { + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: 10 * SHARE_INIT, + expected_increase: 10 * SHARE_INIT, + ..default() + } + .test::

(); + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: 1 * SHARE_INIT, + expected_increase: 1 * SHARE_INIT, + ..default() + } + .test::(); + + // We then artificialy distribute rewards to the target by increasing the value of the pool + // and minting currency to the staking account (this is not how manual rewards would + // be distributed but whatever). + let rewards = 5 * KILO; + assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards)); + assert_ok!(P::OppositePool::share_stake_among_holders( + &ACCOUNT_CANDIDATE_1, + Stake(rewards) + )); + assert_ok!(Candidates::::add_total_stake( + &ACCOUNT_CANDIDATE_1, + &Stake(rewards) + )); + + Swap { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + requested_amount: SharesOrStake::Stake(5 * SHARE_INIT + 10), + expected_removed: 5 * SHARE_INIT, + // due to 1 target share now being worth a bit more than SHARE_INIT, + // only 4 target shares can be restaked + expected_restaked: 4_020_000, + // remaining amount is put in the leaving pool, rounded down + // to the closest multiple of 3 (test leaving share init value) + expected_leaving: 979_998, + // thus the 2 stake that could not be put in the leaving pool + // are directly released + expected_released: 2, + ..default() + } + .test::

(); + + assert_eq_last_events!(vec![Event::::SwappedPool { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + source_pool: P::target_pool(), + source_shares: 5, + source_stake: 5 * SHARE_INIT, + target_shares: 4, + target_stake: 4_020_000, + pending_leaving: 979_998, + released: 2, + }]); + }) + } +); diff --git a/pallets/pooled-staking/src/tests/manual_rewards.rs b/pallets/pooled-staking/src/tests/manual_rewards.rs new file mode 100644 index 0000000..5998915 --- /dev/null +++ b/pallets/pooled-staking/src/tests/manual_rewards.rs @@ -0,0 +1,158 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use {super::*, crate::PoolsKey}; + +fn pending_rewards(candicate: AccountId, delegator: AccountId) -> Balance { + pools::ManualRewards::::pending_rewards(&candicate, &delegator) + .unwrap() + .0 +} + +#[test] +fn first_delegation_init_checkpoint() { + ExtBuilder::default().build().execute_with(|| { + // Set counter to simulate past rewards. + // New delegator should not receive any rewards when joining + // and their checkpoint should be set to the current counter. + let counter = 10; + crate::Pools::::set( + ACCOUNT_CANDIDATE_1, + &PoolsKey::ManualRewardsCounter, + counter, + ); + + assert_eq!(pending_rewards(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1), 0); + + let amount = 2 * SHARE_INIT; + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: amount, + expected_increase: amount, + ..default() + } + .test::>(); + + let checkpoint = crate::Pools::::get( + ACCOUNT_CANDIDATE_1, + &PoolsKey::ManualRewardsCheckpoint { + delegator: ACCOUNT_DELEGATOR_1, + }, + ); + assert_eq!(checkpoint, counter); + assert_eq!(pending_rewards(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1), 0); + }); +} + +#[test] +fn second_delegation_transfer_rewards() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * SHARE_INIT; + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: amount, + expected_increase: amount, + ..default() + } + .test::>(); + + // Set counter to simulate rewards. + let counter = 10; + crate::Pools::::set( + ACCOUNT_CANDIDATE_1, + &PoolsKey::ManualRewardsCounter, + counter, + ); + + let expected_rewards = 20; // 10 coins (counter) * 2 shares + assert_eq!( + pending_rewards(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1), + expected_rewards + ); + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: amount, + expected_increase: amount, + expected_manual_rewards: expected_rewards, + } + .test::>(); + + let checkpoint = crate::Pools::::get( + ACCOUNT_CANDIDATE_1, + &PoolsKey::ManualRewardsCheckpoint { + delegator: ACCOUNT_DELEGATOR_1, + }, + ); + assert_eq!(checkpoint, counter); + assert_eq!(pending_rewards(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1), 0); + }); +} + +#[test] +fn undelegation_transfer_rewards() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * SHARE_INIT; + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: amount, + expected_increase: amount, + ..default() + } + .test::>(); + + // Set counter to simulate rewards. + let counter = 10; + crate::Pools::::set( + ACCOUNT_CANDIDATE_1, + &PoolsKey::ManualRewardsCounter, + counter, + ); + + let expected_rewards = 20; // 10 coins (counter) * 2 shares + assert_eq!( + pending_rewards(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1), + expected_rewards + ); + + let final_amount = 2 * SHARE_INIT; + let leaving_amount = round_down(final_amount, 3); // test leaving rounding + + RequestUndelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: SharesOrStake::Stake(final_amount), + expected_removed: final_amount, + expected_leaving: leaving_amount, + expected_manual_rewards: expected_rewards, + ..default() + } + .test::>(); + + let checkpoint = crate::Pools::::get( + ACCOUNT_CANDIDATE_1, + &PoolsKey::ManualRewardsCheckpoint { + delegator: ACCOUNT_DELEGATOR_1, + }, + ); + assert_eq!(checkpoint, counter); + assert_eq!(pending_rewards(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1), 0); + }); +} diff --git a/pallets/pooled-staking/src/tests/mod.rs b/pallets/pooled-staking/src/tests/mod.rs new file mode 100644 index 0000000..05d1201 --- /dev/null +++ b/pallets/pooled-staking/src/tests/mod.rs @@ -0,0 +1,588 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +mod candidates; +mod delegator_flow; +mod manual_rewards; +mod rebalance; +mod rewards; + +use { + crate::{ + assert_eq_events, assert_fields_eq, + candidate::Candidates, + mock::*, + pool_test, + pools::{self, Pool}, + AllTargetPool, Error, Event, PendingOperationKey, PendingOperationQuery, PendingOperations, + Shares, SharesOrStake, Stake, TargetPool, + }, + frame_support::{ + assert_noop, assert_ok, + traits::tokens::fungible::{Balanced, Mutate}, + }, + sp_runtime::TokenError, +}; + +pub type Joining = pools::Joining; +pub type Leaving = pools::Leaving; + +pub fn default() -> T { + Default::default() +} + +pub(crate) fn operation_stake( + candidate: AccountId, + delegator: AccountId, + pool: TargetPool, + at: u64, +) -> Balance { + let operation_key = match pool { + TargetPool::AutoCompounding => { + PendingOperationKey::JoiningAutoCompounding { candidate, at } + } + TargetPool::ManualRewards => PendingOperationKey::JoiningManualRewards { candidate, at }, + }; + + let shares = PendingOperations::::get(delegator, operation_key); + if shares == 0 { + return 0; + } + + Joining::shares_to_stake(&candidate, Shares(shares)) + .unwrap() + .0 +} + +#[must_use] +pub(crate) struct RequestDelegation { + candidate: AccountId, + delegator: AccountId, + pool: TargetPool, + amount: Balance, + expected_joining: Balance, +} + +impl RequestDelegation { + pub fn test(self) { + let Self { + candidate, + delegator, + pool, + amount, + expected_joining, + } = self; + + let now = block_number(); + + let before = State::extract(candidate, delegator); + let pool_before = PoolState::extract::(candidate, delegator); + let operation_before = operation_stake(candidate, delegator, pool, now); + + assert_ok!(Staking::request_delegate( + RuntimeOrigin::signed(delegator), + candidate, + pool, + amount, + )); + + let after = State::extract(candidate, delegator); + let pool_after = PoolState::extract::(candidate, delegator); + let operation_after = operation_stake(candidate, delegator, pool, now); + + // Actual balances don't change + assert_fields_eq!(before, after, [delegator_balance, staking_balance]); + assert_eq!( + before.delegator_hold + expected_joining, + after.delegator_hold + ); + assert_eq!(pool_before.hold + expected_joining, pool_after.hold); + assert_eq!(pool_before.stake + expected_joining, pool_after.stake); + assert_eq!( + before.candidate_total_stake + expected_joining, + after.candidate_total_stake + ); + assert_eq!(operation_before + expected_joining, operation_after); + } +} + +#[must_use] +#[derive(Default)] +pub(crate) struct ExecuteDelegation { + candidate: AccountId, + delegator: AccountId, + block_number: u64, + expected_increase: Balance, + expected_manual_rewards: Balance, +} + +impl ExecuteDelegation { + pub fn test>(self) { + let Self { + candidate, + delegator, + block_number, + expected_increase, + expected_manual_rewards, + } = self; + + let before = State::extract(candidate, delegator); + let joining_before = PoolState::extract::(candidate, delegator); + let pool_before = PoolState::extract::

(candidate, delegator); + let request_before = crate::PendingOperations::::get( + delegator, + P::joining_operation_key(candidate, block_number), + ); + let request_before = + pools::Joining::::shares_to_stake(&candidate, Shares(request_before)) + .unwrap() + .0; + + let refund = request_before + .checked_sub(expected_increase) + .expect("expected increase should be <= requested amount"); + + assert_ok!(Staking::execute_pending_operations( + RuntimeOrigin::signed(delegator), + vec![PendingOperationQuery { + delegator, + operation: P::joining_operation_key(candidate, block_number) + }] + )); + + let after = State::extract(candidate, delegator); + let joining_after = PoolState::extract::(candidate, delegator); + let pool_after = PoolState::extract::

(candidate, delegator); + let request_after = crate::PendingOperations::::get( + delegator, + P::joining_operation_key(candidate, block_number), + ); + + // Actual balances changes only due to manuyal rewards. + assert_eq!( + before.delegator_balance + expected_manual_rewards, + after.delegator_balance + ); + assert_eq!( + before.staking_balance - expected_manual_rewards, + after.staking_balance + ); + // However funds are held (with share rounding released) + assert_eq!(request_after, 0); + + assert_eq!(before.delegator_hold - refund, after.delegator_hold); + assert_eq!( + before.candidate_total_stake - refund, + after.candidate_total_stake + ); + + assert_eq!(joining_before.hold - request_before, joining_after.hold); + assert_eq!(joining_before.stake - request_before, joining_after.stake); + + assert_eq!(pool_before.hold + expected_increase, pool_after.hold); + assert_eq!(pool_before.stake + expected_increase, pool_after.stake); + } +} + +#[must_use] +#[derive(Default)] +pub(crate) struct FullDelegation { + candidate: AccountId, + delegator: AccountId, + request_amount: Balance, + expected_increase: Balance, + expected_manual_rewards: Balance, +} + +impl FullDelegation { + pub fn test>(self) { + let Self { + candidate, + delegator, + request_amount, + expected_increase, + expected_manual_rewards, + } = self; + + let block_number = block_number(); + + RequestDelegation { + candidate, + delegator, + pool: P::target_pool(), + amount: request_amount, + expected_joining: round_down(request_amount, 2), + } + .test(); + + roll_to(block_number + BLOCKS_TO_WAIT); + + ExecuteDelegation { + candidate, + delegator, + block_number, + expected_increase, + expected_manual_rewards, + } + .test::

(); + } +} + +#[must_use] +pub(crate) struct RequestUndelegation { + candidate: AccountId, + delegator: AccountId, + request_amount: SharesOrStake, + expected_removed: Balance, + expected_leaving: Balance, + expected_manual_rewards: Balance, + expected_hold_rebalance: Balance, +} + +impl Default for RequestUndelegation { + fn default() -> Self { + Self { + candidate: 0, + delegator: 0, + request_amount: SharesOrStake::Stake(0), + expected_removed: 0, + expected_leaving: 0, + expected_manual_rewards: 0, + expected_hold_rebalance: 0, + } + } +} + +impl RequestUndelegation { + pub fn test>(self) { + let Self { + candidate, + delegator, + request_amount, + expected_removed, + expected_leaving, + expected_manual_rewards, + expected_hold_rebalance, + } = self; + + let dust = expected_removed + .checked_sub(expected_leaving) + .expect("should removed >= leaving"); + + let before = State::extract(candidate, delegator); + let pool_before = PoolState::extract::

(candidate, delegator); + let leaving_before = PoolState::extract::(candidate, delegator); + + assert_ok!(Staking::request_undelegate( + RuntimeOrigin::signed(delegator), + candidate, + P::target_pool(), + request_amount, + )); + + let after = State::extract(candidate, delegator); + let pool_after = PoolState::extract::

(candidate, delegator); + let leaving_after = PoolState::extract::(candidate, delegator); + + // Actual balances changes due to manual rewards and hold rebalance. + assert_eq!( + before.delegator_balance + expected_manual_rewards + expected_hold_rebalance, + after.delegator_balance + ); + assert_eq!( + before.staking_balance - expected_manual_rewards - expected_hold_rebalance, + after.staking_balance + ); + // Dust is released immediately. + assert_eq!( + before.delegator_hold - dust + expected_hold_rebalance, + after.delegator_hold + ); + // Pool decrease. + assert_eq!(pool_before.stake - expected_removed, pool_after.stake); + assert_eq!( + pool_before.hold + expected_hold_rebalance - expected_removed, + pool_after.stake + ); + // Leaving increase. + assert_eq!(leaving_before.stake + expected_leaving, leaving_after.stake); + assert_eq!(leaving_before.hold + expected_leaving, leaving_after.stake); + // Stake no longer contribute to election + assert_eq!( + before.candidate_total_stake - expected_removed, + after.candidate_total_stake + ); + } +} + +#[must_use] +#[derive(Default)] +pub(crate) struct ExecuteUndelegation { + candidate: AccountId, + delegator: AccountId, + block_number: u64, + expected_decrease: Balance, +} + +impl ExecuteUndelegation { + pub fn test(self) { + let Self { + candidate, + delegator, + block_number, + expected_decrease, + } = self; + + let before = State::extract(candidate, delegator); + let leaving_before = PoolState::extract::(candidate, delegator); + + assert_ok!(Staking::execute_pending_operations( + RuntimeOrigin::signed(delegator), + vec![PendingOperationQuery { + delegator, + operation: PendingOperationKey::Leaving { + candidate, + at: block_number + } + }] + )); + + let after = State::extract(candidate, delegator); + let leaving_after = PoolState::extract::(candidate, delegator); + let request_after = crate::PendingOperations::::get( + delegator, + PendingOperationKey::Leaving { + candidate, + at: block_number, + }, + ); + + // Actual balances don't change + assert_fields_eq!(before, after, [delegator_balance, staking_balance]); + assert_eq!(request_after, 0); + assert_eq!( + before.delegator_hold - expected_decrease, + after.delegator_hold + ); + assert_fields_eq!(before, after, candidate_total_stake); + assert_eq!(leaving_before.hold - expected_decrease, leaving_after.hold); + assert_eq!( + leaving_before.stake - expected_decrease, + leaving_after.stake + ); + } +} + +#[must_use] +pub(crate) struct FullUndelegation { + candidate: AccountId, + delegator: AccountId, + request_amount: SharesOrStake, + expected_removed: Balance, + expected_leaving: Balance, + expected_hold_rebalance: Balance, +} + +impl Default for FullUndelegation { + fn default() -> Self { + Self { + candidate: 0, + delegator: 0, + request_amount: SharesOrStake::Stake(0), + expected_removed: 0, + expected_leaving: 0, + expected_hold_rebalance: 0, + } + } +} + +impl FullUndelegation { + pub fn test>(self) { + let Self { + candidate, + delegator, + request_amount, + expected_removed, + expected_leaving, + expected_hold_rebalance, + } = self; + + let block_number = block_number(); + RequestUndelegation { + candidate, + delegator, + request_amount, + expected_removed, + expected_leaving, + expected_hold_rebalance, + ..default() + } + .test::

(); + + roll_to(block_number + BLOCKS_TO_WAIT); + + ExecuteUndelegation { + candidate, + delegator, + block_number, + expected_decrease: expected_leaving, + } + .test(); + } +} + +pub(crate) fn do_rebalance_hold>( + candidate: AccountId, + delegator: AccountId, + target_pool: AllTargetPool, + expected_rebalance: SignedBalance, +) { + let before = State::extract(candidate, delegator); + let pool_before = PoolState::extract::

(candidate, delegator); + + assert_ok!(Staking::rebalance_hold( + RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), + ACCOUNT_CANDIDATE_1, + ACCOUNT_DELEGATOR_1, + target_pool + )); + + let after = State::extract(candidate, delegator); + let pool_after = PoolState::extract::

(candidate, delegator); + + // Balances should update + match expected_rebalance { + SignedBalance::Positive(balance) => { + assert_eq!(pool_before.hold + balance, pool_after.hold); + assert_eq!(before.delegator_balance + balance, after.delegator_balance); + assert_eq!(before.staking_balance - balance, after.staking_balance); + } + SignedBalance::Negative(balance) => { + assert_eq!(pool_before.hold - balance, pool_after.hold); + assert_eq!(before.delegator_balance - balance, after.delegator_balance); + assert_eq!(before.staking_balance + balance, after.staking_balance); + } + } + + // Stake stay the same. + assert_fields_eq!(pool_before, pool_after, stake); +} + +pub(crate) fn currency_issue(amount: Balance) -> crate::CreditOf { + <::Currency as Balanced>::issue(amount) +} + +#[must_use] +pub(crate) struct Swap { + candidate: AccountId, + delegator: AccountId, + requested_amount: SharesOrStake, + + expected_removed: Balance, + expected_restaked: Balance, + expected_leaving: Balance, + expected_released: Balance, + expected_hold_rebalance: Balance, +} + +impl Default for Swap { + fn default() -> Self { + Self { + candidate: 0, + delegator: 0, + requested_amount: SharesOrStake::Stake(0), + expected_removed: 0, + expected_restaked: 0, + expected_leaving: 0, + expected_released: 0, + expected_hold_rebalance: 0, + } + } +} + +impl Swap { + pub fn test>(self) { + let Self { + candidate, + delegator, + requested_amount, + expected_removed, + expected_restaked, + expected_leaving, + expected_released, + expected_hold_rebalance, + } = self; + + let before = State::extract(candidate, delegator); + let source_pool_before = PoolState::extract::

(candidate, delegator); + let target_pool_before = PoolState::extract::(candidate, delegator); + let leaving_before = PoolState::extract::(candidate, delegator); + + assert_ok!(Staking::swap_pool( + RuntimeOrigin::signed(delegator), + candidate, + P::target_pool(), + requested_amount + )); + + let after = State::extract(candidate, delegator); + let source_pool_after = PoolState::extract::

(candidate, delegator); + let target_pool_after = PoolState::extract::(candidate, delegator); + let leaving_after = PoolState::extract::(candidate, delegator); + + // Actual balances changes due to hold rebalance. + assert_eq!( + before.delegator_balance + expected_hold_rebalance, + after.delegator_balance + ); + assert_eq!( + before.staking_balance - expected_hold_rebalance, + after.staking_balance + ); + + // Pool change. + assert_eq!( + source_pool_before.stake - expected_removed, + source_pool_after.stake + ); + assert_eq!( + source_pool_before.hold + expected_hold_rebalance - expected_removed, + source_pool_after.stake + ); + + assert_eq!( + target_pool_before.stake + expected_restaked, + target_pool_after.stake + ); + assert_eq!( + target_pool_before.hold + expected_restaked, + target_pool_after.hold + ); + + assert_eq!(leaving_before.stake + expected_leaving, leaving_after.stake); + assert_eq!(leaving_before.hold + expected_leaving, leaving_after.stake); + + assert_eq!( + before.candidate_total_stake - expected_leaving - expected_released, + after.candidate_total_stake + ); + // Dust is released immediately. + assert_eq!( + before.delegator_hold - expected_released + expected_hold_rebalance, + after.delegator_hold + ); + } +} diff --git a/pallets/pooled-staking/src/tests/rebalance.rs b/pallets/pooled-staking/src/tests/rebalance.rs new file mode 100644 index 0000000..a1e3597 --- /dev/null +++ b/pallets/pooled-staking/src/tests/rebalance.rs @@ -0,0 +1,258 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use super::*; + +pool_test!( + fn rebalance_increase

() { + ExtBuilder::default().build().execute_with(|| { + // Preparation: + // We naturaly delegate towards a candidate. + let initial_amount = 2 * SHARE_INIT; + let rewards = 5 * KILO; + let final_amount = initial_amount + rewards; + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: initial_amount, + expected_increase: initial_amount, + ..default() + } + .test::

(); + + // We then artificialy distribute rewards by increasing the value of the pool + // and minting currency to the staking account (this is not how manual rewards would + // be distributed but whatever). + assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards)); + assert_ok!(P::share_stake_among_holders( + &ACCOUNT_CANDIDATE_1, + Stake(rewards) + )); + assert_ok!(Candidates::::add_total_stake( + &ACCOUNT_CANDIDATE_1, + &Stake(rewards) + )); + assert_eq!(total_balance(&ACCOUNT_STAKING), DEFAULT_BALANCE + rewards); + + // Holds should not change but the computed stake should increase. + assert_eq!(total_balance(&ACCOUNT_DELEGATOR_1), 1 * DEFAULT_BALANCE); + assert_eq!(balance_hold(&ACCOUNT_DELEGATOR_1), initial_amount); + assert_eq!( + P::hold(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1), + Stake(initial_amount) + ); + assert_eq!( + P::shares(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1), + Shares(2) + ); + assert_eq!( + P::computed_stake(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1) + .unwrap() + .0, + final_amount + ); + assert_eq!( + Candidates::::total_stake(&ACCOUNT_CANDIDATE_1), + Stake(final_amount) + ); + + // We perform the rebalancing and check it works. + do_rebalance_hold::

( + ACCOUNT_CANDIDATE_1, + ACCOUNT_DELEGATOR_1, + P::target_pool().into(), + SignedBalance::Positive(rewards), + ); + }) + } +); + +pool_test!( + fn rebalance_decrease

() { + ExtBuilder::default().build().execute_with(|| { + // Preparation: + // We naturaly delegate towards a candidate. + let initial_amount = 2 * SHARE_INIT; + let slash = 5 * KILO; + let final_amount = initial_amount - slash; + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: initial_amount, + expected_increase: initial_amount, + ..default() + } + .test::

(); + + // We then artificialy slash by decreasing the value of the pool. + assert_ok!(P::slash_stake_among_holders( + &ACCOUNT_CANDIDATE_1, + Stake(slash) + )); + assert_ok!(Candidates::::sub_total_stake( + &ACCOUNT_CANDIDATE_1, + Stake(slash) + )); + assert_eq!(total_balance(&ACCOUNT_STAKING), DEFAULT_BALANCE); // didn't change + + // Holds should not change but the computed stake should decrease. + assert_eq!(total_balance(&ACCOUNT_DELEGATOR_1), 1 * DEFAULT_BALANCE); + assert_eq!(balance_hold(&ACCOUNT_DELEGATOR_1), initial_amount); + assert_eq!( + P::hold(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1), + Stake(initial_amount) + ); + assert_eq!( + P::shares(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1), + Shares(2) + ); + assert_eq!( + P::computed_stake(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1) + .unwrap() + .0, + final_amount + ); + assert_eq!( + Candidates::::total_stake(&ACCOUNT_CANDIDATE_1), + Stake(final_amount) + ); + + // We perform the rebalancing and check it works. + do_rebalance_hold::

( + ACCOUNT_CANDIDATE_1, + ACCOUNT_DELEGATOR_1, + P::target_pool().into(), + SignedBalance::Negative(slash), + ); + }) + } +); + +pool_test!( + fn rebalance_noop

() { + ExtBuilder::default().build().execute_with(|| { + // Preparation: + // We naturaly delegate towards a candidate. + let initial_amount = 2 * SHARE_INIT; + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: initial_amount, + expected_increase: initial_amount, + ..default() + } + .test::

(); + + // We perform the rebalancing and check nothing happen. + do_rebalance_hold::

( + ACCOUNT_CANDIDATE_1, + ACCOUNT_DELEGATOR_1, + P::target_pool().into(), + SignedBalance::Positive(0), + ); + }) + } +); + +pool_test!( + fn rebalance_in_undelegation_request

() { + ExtBuilder::default().build().execute_with(|| { + let joining_amount = 2 * SHARE_INIT; + let rewards = 5 * KILO; + let leaving_requested_amount = joining_amount + rewards; + let leaving_amount = round_down(leaving_requested_amount, 3); // test leaving rounding + + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: joining_amount, + expected_increase: joining_amount, + ..default() + } + .test::

(); + + // We then artificialy distribute rewards by increasing the value of the pool + // and minting currency to the staking account (this is not how manual rewards would + // be distributed but whatever). + assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards)); + assert_ok!(P::share_stake_among_holders( + &ACCOUNT_CANDIDATE_1, + Stake(rewards) + )); + assert_ok!(Candidates::::add_total_stake( + &ACCOUNT_CANDIDATE_1, + &Stake(rewards) + )); + assert_eq!(total_balance(&ACCOUNT_STAKING), DEFAULT_BALANCE + rewards); + + // We then do the undelegation + RequestUndelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: SharesOrStake::Stake(leaving_requested_amount), + expected_removed: leaving_requested_amount, + expected_leaving: leaving_amount, + expected_hold_rebalance: rewards, + ..default() + } + .test::

(); + }) + } +); + +pool_test!( + fn rebalance_in_swap

() { + ExtBuilder::default().build().execute_with(|| { + FullDelegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + request_amount: 10 * SHARE_INIT, + expected_increase: 10 * SHARE_INIT, + ..default() + } + .test::

(); + + // We then artificialy distribute rewards to the source pool by increasing the value of the pool + // and minting currency to the staking account (this is not how manual rewards would + // be distributed but whatever). + let rewards = 2 * SHARE_INIT; + assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards)); + assert_ok!(P::share_stake_among_holders( + &ACCOUNT_CANDIDATE_1, + Stake(rewards) + )); + assert_ok!(Candidates::::add_total_stake( + &ACCOUNT_CANDIDATE_1, + &Stake(rewards) + )); + + Swap { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + requested_amount: SharesOrStake::Shares(9), + expected_removed: 10_800_000, + expected_restaked: 10_000_000, + expected_leaving: 799998, + expected_released: 2, + expected_hold_rebalance: rewards, + } + .test::

(); + }) + } +); diff --git a/pallets/pooled-staking/src/tests/rewards.rs b/pallets/pooled-staking/src/tests/rewards.rs new file mode 100644 index 0000000..82eeab5 --- /dev/null +++ b/pallets/pooled-staking/src/tests/rewards.rs @@ -0,0 +1,637 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + super::*, + crate::{ + assert_eq_last_events, + pools::{AutoCompounding, ManualRewards}, + Pallet, TargetPool, + }, + frame_support::assert_err, + sp_runtime::DispatchError, + tp_traits::DistributeRewards, +}; + +struct Delegation { + candidate: AccountId, + delegator: AccountId, + pool: TargetPool, + stake: Balance, +} + +struct RewardRequest { + collator: AccountId, + rewards: Balance, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct DelegatorState { + candidate: AccountId, + delegator: AccountId, + auto_stake: Balance, + auto_shares: Balance, + manual_stake: Balance, + manual_shares: Balance, + pending_rewards: Balance, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Distribution { + collator_auto: Balance, + collator_manual: Balance, + delegators_auto: Balance, + delegators_manual: Balance, +} + +fn test_distribution( + delegations: &[Delegation], + reward: RewardRequest, + stakes: &[DelegatorState], + distribution: Distribution, +) { + use crate::traits::Timer; + let block_number = ::JoiningRequestTimer::now(); + + // Create new supply for rewards + let new_supply = currency_issue(reward.rewards); + use frame_support::traits::Imbalance; + let new_supply_amount = new_supply.peek(); + + // Request all delegations + for d in delegations { + assert_ok!(Staking::request_delegate( + RuntimeOrigin::signed(d.delegator), + d.candidate, + d.pool, + d.stake, + )); + } + + // Wait for delegation to be executable + for _ in 0..BLOCKS_TO_WAIT { + roll_one_block(); + } + + // Execute delegations + for d in delegations { + assert_ok!(Staking::execute_pending_operations( + RuntimeOrigin::signed(d.delegator), + vec![PendingOperationQuery { + delegator: d.delegator, + operation: match d.pool { + TargetPool::AutoCompounding => PendingOperationKey::JoiningAutoCompounding { + candidate: d.candidate, + at: block_number + }, + TargetPool::ManualRewards => PendingOperationKey::JoiningManualRewards { + candidate: d.candidate, + at: block_number + }, + } + }] + )); + } + + // Distribute rewards + let candidate_balance_before = total_balance(&ACCOUNT_CANDIDATE_1); + assert_ok!(Pallet::::distribute_rewards( + reward.collator, + new_supply + )); + let candidate_balance_after = total_balance(&ACCOUNT_CANDIDATE_1); + + // Check events matches the expected distribution. + assert_eq_last_events!(vec![ + Event::::RewardedCollator { + collator: reward.collator, + auto_compounding_rewards: distribution.collator_auto, + manual_claim_rewards: distribution.collator_manual, + }, + Event::RewardedDelegators { + collator: reward.collator, + auto_compounding_rewards: distribution.delegators_auto, + manual_claim_rewards: distribution.delegators_manual, + }, + ]); + + // Check the state of each delegate match the expected values. + for expected in stakes { + let actual = DelegatorState { + candidate: expected.candidate, + delegator: expected.delegator, + auto_stake: AutoCompounding::::computed_stake( + &expected.candidate, + &expected.delegator, + ) + .expect("to have stake") + .0, + auto_shares: AutoCompounding::::shares( + &expected.candidate, + &expected.delegator, + ) + .0, + manual_stake: ManualRewards::::computed_stake( + &expected.candidate, + &expected.delegator, + ) + .expect("to have stake") + .0, + manual_shares: ManualRewards::::shares( + &expected.candidate, + &expected.delegator, + ) + .0, + pending_rewards: ManualRewards::::pending_rewards( + &expected.candidate, + &expected.delegator, + ) + .expect("no overflow") + .0, + }; + + similar_asserts::assert_eq!(&actual, expected); + } + + // Additional checks. + assert_eq!( + distribution.collator_auto + + distribution.collator_manual + + distribution.delegators_auto + + distribution.delegators_manual, + new_supply_amount, + "Distribution total doesn't match requested reward" + ); + + assert_eq!( + candidate_balance_before + distribution.collator_manual, + candidate_balance_after, + "candidate balance should be increased by collator_manual" + ); + + let sum_manual: Balance = stakes.iter().map(|s| s.pending_rewards).sum(); + assert_eq!( + sum_manual, distribution.delegators_manual, + "sum of pending rewards should match distributed delegators manual rewards" + ); + + let sum_auto_stake_before: Balance = delegations + .iter() + .filter_map(|d| match d.pool { + TargetPool::AutoCompounding => Some(d.stake), + _ => None, + }) + .sum(); + + let sum_auto_stake_after = AutoCompounding::::total_staked(&reward.collator).0; + assert_eq!( + sum_auto_stake_after - sum_auto_stake_before, + distribution.collator_auto + distribution.delegators_auto, + "diff between sum of auto stake before and after distribution should match distributed auto rewards" + ); +} + +#[test] +fn candidate_only_manual_only() { + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::ManualRewards, + stake: 1_000_000_000, + }], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 1_000_000, + }, + &[DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + auto_shares: 0, + auto_stake: 0, + manual_shares: 1_000, + manual_stake: 1_000_000_000, + pending_rewards: 800_000, + }], + Distribution { + collator_auto: 0, + collator_manual: 200_000, // 20% of rewards + delegators_auto: 0, + delegators_manual: 800_000, // 80% of rewards + }, + ) + }); +} + +#[test] +fn candidate_only_auto_only() { + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::AutoCompounding, + stake: 1_000_000_000, + }], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 10_000_000, + }, + &[DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + auto_shares: 1_001, + // initial auto stake is 1_000_000_000 for + // 8_000_000 is shared between all delegators, so 1 share + // is now worth 1_008_000_000 / 1000 = 1_008_000 now + // collator is should be rewarded 2_000_000 in auto shares, + // which allows to get 1 more share, so the collator now + // have 1_001 shares worth + // 1_008_000_000 + 1_008_000 = 1_009_008_000 + auto_stake: 1_009_008_000, + manual_shares: 0, + manual_stake: 0, + pending_rewards: 0, + }], + Distribution { + // 20% of rewards, rounded down to closest amount of Auto shares + // AFTER delegators rewards has been rewarded + collator_auto: 1_008_000, + // dust from collator_auto + collator_manual: 992_000, + delegators_auto: 8_000_000, // 80% of rewards + delegators_manual: 0, + }, + ) + }); +} + +#[test] +fn candidate_only_mixed() { + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[ + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::AutoCompounding, + stake: 1_000_000_000, + }, + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::ManualRewards, + stake: 250_000_000, + }, + ], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 10_000_000, + }, + &[DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + auto_shares: 1_001, + auto_stake: 1_007_406_400, + manual_shares: 250, + manual_stake: 250_000_000, + pending_rewards: 1_600_000, + }], + Distribution { + collator_auto: 1_006_400, + collator_manual: 993_600, + delegators_auto: 6_400_000, + delegators_manual: 1_600_000, + }, + ) + }); +} + +#[test] +fn delegators_manual_only() { + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[ + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::ManualRewards, + stake: 1_000_000_000, + }, + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: TargetPool::ManualRewards, + stake: 250_000_000, + }, + ], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 10_000_000, + }, + &[ + DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + auto_shares: 0, + auto_stake: 0, + manual_shares: 1_000, + manual_stake: 1_000_000_000, + pending_rewards: 6_400_000, + }, + DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + auto_shares: 0, + auto_stake: 0, + manual_shares: 250, + manual_stake: 250_000_000, + pending_rewards: 1_600_000, + }, + ], + Distribution { + collator_auto: 0, + collator_manual: 2_000_000, + delegators_auto: 0, + delegators_manual: 8_000_000, + }, + ) + }); +} + +#[test] +fn delegators_auto_only() { + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[ + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::AutoCompounding, + stake: 1_000_000_000, + }, + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: TargetPool::AutoCompounding, + stake: 250_000_000, + }, + ], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 10_000_000, + }, + &[ + DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + auto_shares: 1_001, + auto_stake: 1_007_406_400, + manual_shares: 0, + manual_stake: 0, + pending_rewards: 0, + }, + DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + auto_shares: 250, + auto_stake: 251_600_000, + manual_shares: 0, + manual_stake: 0, + pending_rewards: 0, + }, + ], + Distribution { + collator_auto: 1_006_400, + collator_manual: 993_600, + delegators_auto: 8_000_000, + delegators_manual: 0, + }, + ) + }); +} + +#[test] +fn delegators_mixed() { + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[ + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::AutoCompounding, + stake: 1_000_000_000, + }, + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + pool: TargetPool::ManualRewards, + stake: 500_000_000, + }, + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: TargetPool::ManualRewards, + stake: 250_000_000, + }, + Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: TargetPool::AutoCompounding, + stake: 500_000_000, + }, + ], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 10_000_000, + }, + &[ + DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_CANDIDATE_1, + auto_shares: 1_001, + auto_stake: 1_004_559_388, + manual_shares: 500, + manual_stake: 500_000_000, + pending_rewards: 1_777_500, + }, + DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + auto_shares: 500, + auto_stake: 501_777_916, + manual_shares: 250, + manual_stake: 250_000_000, + pending_rewards: 888_750, + }, + ], + Distribution { + collator_auto: 1_003_555, + collator_manual: 996_445, + // Total stake: 2_250_000_000 + // Auto stake: 1_500_000_000 + // Manual stake: 750_000_000 + // Manual shares: 750 + // Rewards towards delegators: 80% of 10_000_000 = 8_000_000 + // Rewards towards manual deleg + // = 8_000_000 * 750_000_000 / 2_250_000_000 + // = 2_666_666 + // => 2_666_250 (rounding down to closest multiple of 750) + // gives dust of 2_666_666 - 2_666_250 = 416 + delegators_manual: 2_666_250, + // Rewards towards auto deleg + // = Rewards deleg - Rewards manual deleg + // = 8_000_000 - 2_666_250 + // = 5_333_750 + delegators_auto: 5_333_750, + }, + ); + }); +} + +#[test] +fn candidate_only_no_stake() { + // Rewarding a candidate that does not have any stake works + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 1_000_000, + }, + &[], + Distribution { + collator_auto: 0, + collator_manual: 1_000_000, // 100% of rewards + delegators_auto: 0, + delegators_manual: 0, // 0% of rewards + }, + ) + }); +} + +#[test] +fn delegator_only_candidate_zero() { + // Rewarding a candidate that does not have any stake works + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: TargetPool::ManualRewards, + stake: 250_000_000, + }], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 1_000_000, + }, + &[DelegatorState { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + auto_shares: 0, + auto_stake: 0, + manual_shares: 250, + manual_stake: 250_000_000, + pending_rewards: 800_000, + }], + Distribution { + collator_auto: 0, + collator_manual: 200_000, // 20% of rewards + delegators_auto: 0, + delegators_manual: 800_000, // 80% of rewards + }, + ) + }); +} + +#[test] +fn delegator_only_candidate_no_stake_auto_compounding() { + // Rewarding a candidate that does not have any stake, while some delegator + // has stake for that candidate + ExtBuilder::default().build().execute_with(|| { + test_distribution( + &[Delegation { + candidate: ACCOUNT_CANDIDATE_1, + delegator: ACCOUNT_DELEGATOR_1, + pool: TargetPool::AutoCompounding, + stake: 250_000_000, + }], + RewardRequest { + collator: ACCOUNT_CANDIDATE_1, + rewards: 1_000_000, + }, + &[], + Distribution { + collator_auto: 0, + collator_manual: 200_000, // 20% of rewards + delegators_auto: 800_000, // 80% of rewards + delegators_manual: 0, + }, + ) + }); +} + +#[test] +fn reward_distribution_is_transactional() { + ExtBuilder::default().build().execute_with(|| { + use crate::traits::Timer; + let request_time = ::JoiningRequestTimer::now(); + + assert_ok!(Staking::request_delegate( + RuntimeOrigin::signed(ACCOUNT_CANDIDATE_1), + ACCOUNT_CANDIDATE_1, + TargetPool::AutoCompounding, + 1_000_000_000, + )); + + // Wait for delegation to be executable + for _ in 0..BLOCKS_TO_WAIT { + roll_one_block(); + } + + assert_ok!(Staking::execute_pending_operations( + RuntimeOrigin::signed(ACCOUNT_CANDIDATE_1), + vec![PendingOperationQuery { + delegator: ACCOUNT_CANDIDATE_1, + operation: PendingOperationKey::JoiningAutoCompounding { + candidate: ACCOUNT_CANDIDATE_1, + at: request_time + }, + }] + )); + + let total_staked_before = + pools::AutoCompounding::::total_staked(&ACCOUNT_CANDIDATE_1); + + // Increase ED to make reward destribution fail when resolving + // credit to Staking account. + MockExistentialDeposit::set(u128::MAX); + + let rewards = Balances::issue(1_000_000_000); + assert_err!( + Staking::distribute_rewards(ACCOUNT_CANDIDATE_1, rewards), + DispatchError::NoProviders + ); + + let total_staked_after = + pools::AutoCompounding::::total_staked(&ACCOUNT_CANDIDATE_1); + assert_eq!( + total_staked_before, total_staked_after, + "distribution should be reverted" + ); + }) +} diff --git a/pallets/pooled-staking/src/traits.rs b/pallets/pooled-staking/src/traits.rs new file mode 100644 index 0000000..f597066 --- /dev/null +++ b/pallets/pooled-staking/src/traits.rs @@ -0,0 +1,101 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + core::{fmt::Debug, marker::PhantomData}, + frame_system::pallet_prelude::BlockNumberFor, + parity_scale_codec::FullCodec, + scale_info::TypeInfo, + sp_runtime::traits::{CheckedAdd, Get}, +}; + +/// Allows to get the current instant and check if some duration is elapsed. +pub trait Timer { + /// Type for the instant. Must implement some traits to be used easily with + /// the Pooled Staking pallet. + type Instant: FullCodec + TypeInfo + Clone + Debug + Eq; + + /// Get the current instant. + fn now() -> Self::Instant; + + /// Check if the timer started at `started` is elapsed. + fn is_elapsed(start: &Self::Instant) -> bool; + + /// Returns an instant that will make `is_elapsed` true. + #[cfg(feature = "runtime-benchmarks")] + fn elapsed_instant() -> Self::Instant; + + /// Skip to a state where `now` will make `is_elapsed` true. + #[cfg(feature = "runtime-benchmarks")] + fn skip_to_elapsed(); +} + +/// A timer using block numbers. +/// `T` is the Runtime type while `G` is a getter for the delay. +pub struct BlockNumberTimer(PhantomData<(T, G)>); + +impl Timer for BlockNumberTimer +where + T: frame_system::Config, + G: Get>, +{ + type Instant = BlockNumberFor; + + fn now() -> Self::Instant { + frame_system::Pallet::::block_number() + } + + fn is_elapsed(start: &Self::Instant) -> bool { + let delay = G::get(); + let Some(end) = start.checked_add(&delay) else { + return false; + }; + end <= Self::now() + } + + #[cfg(feature = "runtime-benchmarks")] + fn elapsed_instant() -> Self::Instant { + let delay = G::get(); + Self::now() + .checked_add(&delay) + .expect("overflow when computing valid elapsed instant") + } + + #[cfg(feature = "runtime-benchmarks")] + fn skip_to_elapsed() { + frame_system::Pallet::::set_block_number(Self::elapsed_instant()); + } +} + +/// Allows knowing if some account is eligible to be a candidate. +pub trait IsCandidateEligible { + /// Is the provided account id eligible? + fn is_candidate_eligible(a: &AccountId) -> bool; + + /// Make the provided account id eligible if `eligible` is true, and not + /// eligible if false. + #[cfg(feature = "runtime-benchmarks")] + fn make_candidate_eligible(a: &AccountId, eligible: bool); +} + +impl IsCandidateEligible for () { + fn is_candidate_eligible(_: &AccountId) -> bool { + true + } + + #[cfg(feature = "runtime-benchmarks")] + fn make_candidate_eligible(_: &AccountId, _: bool) {} +} diff --git a/pallets/pooled-staking/src/weights.rs b/pallets/pooled-staking/src/weights.rs new file mode 100644 index 0000000..5af329c --- /dev/null +++ b/pallets/pooled-staking/src/weights.rs @@ -0,0 +1,368 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_pooled_staking +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `pop-os`, CPU: `12th Gen Intel(R) Core(TM) i7-1260P` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet-pooled-staking +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_pooled_staking. +pub trait WeightInfo { + fn request_delegate() -> Weight; + fn execute_pending_operations(b: u32, ) -> Weight; + fn request_undelegate() -> Weight; + fn claim_manual_rewards(b: u32, ) -> Weight; + fn rebalance_hold() -> Weight; + fn update_candidate_position(b: u32, ) -> Weight; + fn swap_pool() -> Weight; + fn distribute_rewards() -> Weight; +} + +/// Weights for pallet_pooled_staking using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: PooledStaking Pools (r:11 w:5) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session NextKeys (r:1 w:0) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PooledStaking PendingOperations (r:1 w:1) + /// Proof Skipped: PooledStaking PendingOperations (max_values: None, max_size: None, mode: Measured) + fn request_delegate() -> Weight { + // Proof Size summary in bytes: + // Measured: `1321` + // Estimated: `29536` + // Minimum execution time: 127_339_000 picoseconds. + Weight::from_parts(133_146_000, 29536) + .saturating_add(T::DbWeight::get().reads(17_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + } + /// Storage: PooledStaking PendingOperations (r:100 w:100) + /// Proof Skipped: PooledStaking PendingOperations (max_values: None, max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PooledStaking Pools (r:1000 w:800) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `b` is `[1, 100]`. + fn execute_pending_operations(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `543 + b * (390 ±0)` + // Estimated: `3593 + b * (25141 ±0)` + // Minimum execution time: 89_544_000 picoseconds. + Weight::from_parts(91_417_000, 3593) + // Standard Error: 630_031 + .saturating_add(Weight::from_parts(99_103_944, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((11_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((9_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 25141).saturating_mul(b.into())) + } + /// Storage: PooledStaking Pools (r:13 w:9) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PooledStaking PendingOperations (r:1 w:1) + /// Proof Skipped: PooledStaking PendingOperations (max_values: None, max_size: None, mode: Measured) + fn request_undelegate() -> Weight { + // Proof Size summary in bytes: + // Measured: `724` + // Estimated: `33889` + // Minimum execution time: 111_997_000 picoseconds. + Weight::from_parts(124_683_000, 33889) + .saturating_add(T::DbWeight::get().reads(16_u64)) + .saturating_add(T::DbWeight::get().writes(11_u64)) + } + /// Storage: PooledStaking Pools (r:300 w:100) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `b` is `[1, 100]`. + fn claim_manual_rewards(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `360 + b * (456 ±0)` + // Estimated: `6196 + b * (7882 ±0)` + // Minimum execution time: 57_580_000 picoseconds. + Weight::from_parts(60_814_000, 6196) + // Standard Error: 421_370 + .saturating_add(Weight::from_parts(55_273_020, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 7882).saturating_mul(b.into())) + } + /// Storage: PooledStaking Pools (r:4 w:1) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + fn rebalance_hold() -> Weight { + // Proof Size summary in bytes: + // Measured: `980` + // Estimated: `11870` + // Minimum execution time: 98_014_000 picoseconds. + Weight::from_parts(128_615_000, 11870) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: PooledStaking Pools (r:600 w:100) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session NextKeys (r:100 w:0) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 100]`. + fn update_candidate_position(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `399 + b * (356 ±0)` + // Estimated: `1881 + b * (15206 ±0)` + // Minimum execution time: 46_082_000 picoseconds. + Weight::from_parts(60_293_000, 1881) + // Standard Error: 131_937 + .saturating_add(Weight::from_parts(35_500_124, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 15206).saturating_mul(b.into())) + } + /// Storage: PooledStaking Pools (r:12 w:8) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + fn swap_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `478` + // Estimated: `31168` + // Minimum execution time: 80_829_000 picoseconds. + Weight::from_parts(97_569_000, 31168) + .saturating_add(T::DbWeight::get().reads(12_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } + /// Storage: PooledStaking Pools (r:9 w:5) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session NextKeys (r:1 w:0) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn distribute_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `1302` + // Estimated: `24567` + // Minimum execution time: 151_254_000 picoseconds. + Weight::from_parts(178_410_000, 24567) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: PooledStaking Pools (r:11 w:5) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session NextKeys (r:1 w:0) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PooledStaking PendingOperations (r:1 w:1) + /// Proof Skipped: PooledStaking PendingOperations (max_values: None, max_size: None, mode: Measured) + fn request_delegate() -> Weight { + // Proof Size summary in bytes: + // Measured: `1321` + // Estimated: `29536` + // Minimum execution time: 127_339_000 picoseconds. + Weight::from_parts(133_146_000, 29536) + .saturating_add(RocksDbWeight::get().reads(17_u64)) + .saturating_add(RocksDbWeight::get().writes(9_u64)) + } + /// Storage: PooledStaking PendingOperations (r:100 w:100) + /// Proof Skipped: PooledStaking PendingOperations (max_values: None, max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PooledStaking Pools (r:1000 w:800) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `b` is `[1, 100]`. + fn execute_pending_operations(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `543 + b * (390 ±0)` + // Estimated: `3593 + b * (25141 ±0)` + // Minimum execution time: 89_544_000 picoseconds. + Weight::from_parts(91_417_000, 3593) + // Standard Error: 630_031 + .saturating_add(Weight::from_parts(99_103_944, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().reads((11_u64).saturating_mul(b.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((9_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 25141).saturating_mul(b.into())) + } + /// Storage: PooledStaking Pools (r:13 w:9) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PooledStaking PendingOperations (r:1 w:1) + /// Proof Skipped: PooledStaking PendingOperations (max_values: None, max_size: None, mode: Measured) + fn request_undelegate() -> Weight { + // Proof Size summary in bytes: + // Measured: `724` + // Estimated: `33889` + // Minimum execution time: 111_997_000 picoseconds. + Weight::from_parts(124_683_000, 33889) + .saturating_add(RocksDbWeight::get().reads(16_u64)) + .saturating_add(RocksDbWeight::get().writes(11_u64)) + } + /// Storage: PooledStaking Pools (r:300 w:100) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `b` is `[1, 100]`. + fn claim_manual_rewards(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `360 + b * (456 ±0)` + // Estimated: `6196 + b * (7882 ±0)` + // Minimum execution time: 57_580_000 picoseconds. + Weight::from_parts(60_814_000, 6196) + // Standard Error: 421_370 + .saturating_add(Weight::from_parts(55_273_020, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(b.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 7882).saturating_mul(b.into())) + } + /// Storage: PooledStaking Pools (r:4 w:1) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(66), added: 2541, mode: MaxEncodedLen) + fn rebalance_hold() -> Weight { + // Proof Size summary in bytes: + // Measured: `980` + // Estimated: `11870` + // Minimum execution time: 98_014_000 picoseconds. + Weight::from_parts(128_615_000, 11870) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: PooledStaking Pools (r:600 w:100) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session NextKeys (r:100 w:0) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 100]`. + fn update_candidate_position(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `399 + b * (356 ±0)` + // Estimated: `1881 + b * (15206 ±0)` + // Minimum execution time: 46_082_000 picoseconds. + Weight::from_parts(60_293_000, 1881) + // Standard Error: 131_937 + .saturating_add(Weight::from_parts(35_500_124, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(b.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 15206).saturating_mul(b.into())) + } + /// Storage: PooledStaking Pools (r:12 w:8) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + fn swap_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `478` + // Estimated: `31168` + // Minimum execution time: 80_829_000 picoseconds. + Weight::from_parts(97_569_000, 31168) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } + /// Storage: PooledStaking Pools (r:9 w:5) + /// Proof Skipped: PooledStaking Pools (max_values: None, max_size: None, mode: Measured) + /// Storage: PooledStaking SortedEligibleCandidates (r:1 w:1) + /// Proof Skipped: PooledStaking SortedEligibleCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session NextKeys (r:1 w:0) + /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn distribute_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `1302` + // Estimated: `24567` + // Minimum execution time: 151_254_000 picoseconds. + Weight::from_parts(178_410_000, 24567) + .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } +} diff --git a/pallets/positive-externality/Cargo.toml b/pallets/positive-externality/Cargo.toml deleted file mode 100644 index 4c9f646..0000000 --- a/pallets/positive-externality/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -name = "positive-externality" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-timestamp = { git = 'https://github.com/paritytech/substrate', branch = "polkadot-v0.9.42", default-features = false } -pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-support = { default-features = false, path = '../support' } -shared-storage = { default-features = false, path="../shared-storage"} -shared-storage-link = { default-features = false, path="../../traits/shared-storage-link"} -schelling-game-shared = {default-features = false, path = "../schelling-game-shared"} -schelling-game-shared-link = {default-features = false, path = "../../traits/schelling-game-shared-link"} -sortition-sum-game = {default-features = false, path="../sortition-sum-game"} - - - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support-test = { version = "3.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "pallet-timestamp/std", - "pallet-balances/std", - "pallet-support/std", - "shared-storage/std", - "schelling-game-shared/std", - "sortition-sum-game/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/positive-externality/positive-externality-rpc/Cargo.toml b/pallets/positive-externality/positive-externality-rpc/Cargo.toml deleted file mode 100644 index a69087d..0000000 --- a/pallets/positive-externality/positive-externality-rpc/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "positive-externality-rpc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } -sc-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-api = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -positive-externality-runtime-api = { default-features= false, path="../positive-externality-runtime-api"} \ No newline at end of file diff --git a/pallets/positive-externality/positive-externality-runtime-api/Cargo.toml b/pallets/positive-externality/positive-externality-runtime-api/Cargo.toml deleted file mode 100644 index 27aec6b..0000000 --- a/pallets/positive-externality/positive-externality-runtime-api/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "positive-externality-runtime-api" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -sp-api = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42"} - -[features] -default = ["std"] -std = [ - "sp-api/std", - "frame-support/std", -] \ No newline at end of file diff --git a/pallets/positive-externality/positive-externality-runtime-api/src/lib.rs b/pallets/positive-externality/positive-externality-runtime-api/src/lib.rs deleted file mode 100644 index b8c2483..0000000 --- a/pallets/positive-externality/positive-externality-runtime-api/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -// use frame_support::sp_std::{vec::Vec}; -// or -use frame_support::sp_std::prelude::*; -use sp_api::codec::Codec; - -sp_api::decl_runtime_apis! { - pub trait PositiveExternalityApi where AccountId: Codec { - - fn get_evidence_period_end_block(user_to_calculate: AccountId) -> Option; - fn get_staking_period_end_block(user_to_calculate: AccountId) -> Option; - fn get_drawing_period_end(user_to_calculate: AccountId) -> (u64, u64, bool); - fn get_commit_period_end_block(user_to_calculate: AccountId) -> Option; - fn get_vote_period_end_block(user_to_calculate: AccountId) -> Option; - fn selected_as_juror(user_to_calculate: AccountId, who: AccountId) -> bool; - } -} diff --git a/pallets/positive-externality/src/benchmarking.rs b/pallets/positive-externality/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/positive-externality/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/positive-externality/src/extras.rs b/pallets/positive-externality/src/extras.rs deleted file mode 100644 index 710f3f8..0000000 --- a/pallets/positive-externality/src/extras.rs +++ /dev/null @@ -1,178 +0,0 @@ -use frame_support::dispatch::DispatchResult; - -use super::*; - -impl Post { - pub fn new(id: PostId, created_by: T::AccountId, content: Content) -> Self { - Post { - id, - created: new_who_and_when::(created_by.clone()), - edited: false, - owner: created_by, - content, - hidden: false, - upvotes_count: 0, - downvotes_count: 0, - } - } - - pub fn ensure_owner(&self, account: &T::AccountId) -> DispatchResult { - ensure!(self.is_owner(account), Error::::NotAPostOwner); - Ok(()) - } - - pub fn is_owner(&self, account: &T::AccountId) -> bool { - self.owner == *account - } -} - -impl Pallet { - pub(super) fn get_phase_data() -> PhaseData { - T::SchellingGameSharedSource::create_phase_data(50, 5, 3, 100, (100, 100)) - } - - pub fn ensure_validation_on_positive_externality(account: T::AccountId) -> DispatchResult { - let bool_data = Validate::::get(account); - ensure!(bool_data == true, Error::::ValidationPositiveExternalityIsOff); - - Ok(()) - } - - pub fn ensure_min_stake_positive_externality(account: T::AccountId) -> DispatchResult { - let stake = StakeBalance::::get(account); - let min_stake = MinimumStake::::get(); - // println!("stake {:?}", stake); - // println!("min stake {:?}", min_stake); - ensure!(stake >= min_stake, Error::::LessThanMinStake); - - Ok(()) - } - - pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { - input.saturated_into::>() - } - - pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { - input.saturated_into::>() - } - - pub(super) fn get_drawn_jurors(user_to_calculate: T::AccountId) -> Vec<(T::AccountId, u64)> { - let pe_block_number = - >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - T::SchellingGameSharedSource::get_drawn_jurors(key) - } - - // Block code start - - pub fn get_evidence_period_end_block(user_to_calculate: T::AccountId) -> Option { - let now = >::block_number(); - - let pe_block_number = - >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_staking_period_end_block(user_to_calculate: T::AccountId) -> Option { - let now = >::block_number(); - - let pe_block_number = - >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_drawing_period_end(user_to_calculate: T::AccountId) -> (u64, u64, bool) { - let pe_block_number = - >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - let phase_data = Self::get_phase_data(); - - let result = - T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); - result - } - - pub fn get_commit_period_end_block(user_to_calculate: T::AccountId) -> Option { - let now = >::block_number(); - - let pe_block_number = - >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_vote_period_end_block(user_to_calculate: T::AccountId) -> Option { - let now = >::block_number(); - - let pe_block_number = - >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn selected_as_juror(user_to_calculate: T::AccountId, who: T::AccountId) -> bool { - let pe_block_number = - >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); - result - } - - // Block code end -} diff --git a/pallets/positive-externality/src/lib.rs b/pallets/positive-externality/src/lib.rs deleted file mode 100644 index 648746b..0000000 --- a/pallets/positive-externality/src/lib.rs +++ /dev/null @@ -1,421 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -mod extras; -pub mod types; -pub use types::{Post, FIRST_POST_ID}; - -use frame_support::sp_runtime::traits::Saturating; -use frame_support::sp_runtime::SaturatedConversion; -use frame_support::sp_std::prelude::*; -use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - ensure, fail, -}; -use frame_support::{ - traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, - PalletId, -}; -use pallet_support::{ - ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, PostId, WhoAndWhen, - WhoAndWhenOf, -}; -use schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType}; -use schelling_game_shared_link::SchellingGameSharedLink; -use shared_storage_link::SharedStorageLink; -use sortition_sum_game::types::SumTreeName; -type AccountIdOf = ::AccountId; -type BalanceOf = <::Currency as Currency>>::Balance; -pub type BlockNumberOf = ::BlockNumber; -pub type SumTreeNameType = SumTreeName, BlockNumberOf>; - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_timestamp::Config + schelling_game_shared::Config - { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - - type SharedStorageSource: SharedStorageLink>; - type SchellingGameSharedSource: SchellingGameSharedLink< - SumTreeName = SumTreeName, - SchellingGameType = SchellingGameType, - BlockNumber = Self::BlockNumber, - AccountId = AccountIdOf, - Balance = BalanceOf, - RangePoint = RangePoint, - Period = Period, - PhaseData = PhaseData, - >; - type Currency: ReservableCurrency; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::type_value] - pub fn DefaultForNextPostId() -> PostId { - FIRST_POST_ID - } - - /// The next post id. - #[pallet::storage] - #[pallet::getter(fn next_post_id)] - pub type NextPostId = StorageValue<_, PostId, ValueQuery, DefaultForNextPostId>; - - /// Get the details of a post by its' id. - #[pallet::storage] - #[pallet::getter(fn post_by_id)] - pub type PostById = StorageMap<_, Twox64Concat, PostId, Post>; - - #[pallet::storage] - #[pallet::getter(fn evidence)] - pub type Evidence = - StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; - - #[pallet::type_value] - pub fn MinimumStake() -> BalanceOf { - 10000u128.saturated_into::>() - } - - #[pallet::storage] - #[pallet::getter(fn user_stake)] - pub type StakeBalance = - StorageMap<_, Twox64Concat, T::AccountId, BalanceOf, ValueQuery>; - - #[pallet::type_value] - pub fn DefaultValidate() -> bool { - true - } - - #[pallet::storage] - #[pallet::getter(fn validate)] - pub type Validate = - StorageMap<_, Twox64Concat, T::AccountId, bool, ValueQuery, DefaultValidate>; - - #[pallet::storage] - #[pallet::getter(fn validation_block)] - pub type ValidationBlock = - StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberOf, ValueQuery>; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { something: u32, who: T::AccountId }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - NotAPostOwner, - ValidationPositiveExternalityIsOff, - LessThanMinStake, - CannotStakeNow, - ChoiceOutOfRange, - } - - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(0)] - pub fn create_positive_externality_post( - origin: OriginFor, - content: Content, - ) -> DispatchResult { - let creator = ensure_signed(origin)?; - - ensure_content_is_valid(content.clone())?; - - // Citizen approved To comment out in production, citizen approved in added in profile validation - - // T::SharedStorageSource::check_citizen_is_approved_link(creator.clone())?; - - let new_post_id = Self::next_post_id(); - - let new_post: Post = Post::new(new_post_id, creator.clone(), content.clone()); - - Evidence::::mutate(creator, |ids| ids.push(new_post_id)); - - PostById::insert(new_post_id, new_post); - NextPostId::::mutate(|n| { - *n += 1; - }); - - // emit event - - Ok(()) - } - - #[pallet::call_index(1)] - #[pallet::weight(0)] - pub fn set_validate_positive_externality( - origin: OriginFor, - value: bool, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - // Check user has done kyc - - Validate::::insert(&who, value); - // emit event - Ok(()) - } - - #[pallet::call_index(2)] - #[pallet::weight(0)] - pub fn apply_staking_period( - origin: OriginFor, - user_to_calculate: T::AccountId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - Self::ensure_validation_on_positive_externality(user_to_calculate.clone())?; - - let stake = MinimumStake::::get(); - - let _ = ::Currency::withdraw( - &who, - stake, - WithdrawReasons::TRANSFER, - ExistenceRequirement::AllowDeath, - )?; - - StakeBalance::::insert(&user_to_calculate, stake); - - let pe_block_number = >::get(user_to_calculate.clone()); - // println!("{:?}", pe_block_number); - let zero_block_number = Self::u64_to_block_saturated(0); - let now = >::block_number(); - let three_month_number = (3 * 30 * 24 * 60 * 60) / 6; - let three_month_block = Self::u64_to_block_saturated(three_month_number); - let modulus = now % three_month_block; - let storage_main_block = now - modulus; - // println!("{:?}", now); - // println!("{:?}", three_month_number); - // println!("{:?}", storage_main_block); - // println!("{:?}", pe_block_number); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate.clone(), - block_number: storage_main_block.clone(), - }; - - // let game_type = SchellingGameType::PositiveExternality; - // || pe_block_number == zero_block_number - - if storage_main_block > pe_block_number || pe_block_number == zero_block_number { - >::insert(user_to_calculate.clone(), storage_main_block); - // check what if called again - T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now)?; - T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; - - // println!("{:?}", data); - } else { - return Err(Error::::CannotStakeNow.into()); - } - - Ok(()) - } - - #[pallet::call_index(3)] - #[pallet::weight(0)] - pub fn apply_jurors( - origin: OriginFor, - user_to_calculate: T::AccountId, - stake: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - Self::ensure_validation_on_positive_externality(user_to_calculate.clone())?; - Self::ensure_min_stake_positive_externality(user_to_calculate.clone())?; - - let pe_block_number = >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::apply_jurors_helper_link(key, phase_data, who, stake)?; - - Ok(()) - } - - #[pallet::call_index(4)] - #[pallet::weight(0)] - pub fn pass_period( - origin: OriginFor, - user_to_calculate: T::AccountId, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - - let pe_block_number = >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let now = >::block_number(); - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; - - Ok(()) - } - - #[pallet::call_index(5)] - #[pallet::weight(0)] - pub fn draw_jurors( - origin: OriginFor, - user_to_calculate: T::AccountId, - iterations: u64, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - - let pe_block_number = >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; - - Ok(()) - } - - // Unstaking - // Stop drawn juror to unstake ✔️ - #[pallet::call_index(6)] - #[pallet::weight(0)] - pub fn unstaking(origin: OriginFor, user_to_calculate: T::AccountId) -> DispatchResult { - let who = ensure_signed(origin)?; - let pe_block_number = >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; - Ok(()) - } - - #[pallet::call_index(7)] - #[pallet::weight(0)] - pub fn commit_vote( - origin: OriginFor, - user_to_calculate: T::AccountId, - vote_commit: [u8; 32], - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let pe_block_number = >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - T::SchellingGameSharedSource::commit_vote_for_score_helper_link(key, who, vote_commit)?; - Ok(()) - } - - #[pallet::call_index(8)] - #[pallet::weight(0)] - pub fn reveal_vote( - origin: OriginFor, - user_to_calculate: T::AccountId, - choice: i64, - salt: Vec, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - ensure!(choice <= 5 && choice >= 1, Error::::ChoiceOutOfRange); - - let pe_block_number = >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate, - block_number: pe_block_number.clone(), - }; - - T::SchellingGameSharedSource::reveal_vote_score_helper_link(key, who, choice, salt)?; - Ok(()) - } - - #[pallet::call_index(9)] - #[pallet::weight(0)] - pub fn get_incentives( - origin: OriginFor, - user_to_calculate: T::AccountId, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - let pe_block_number = >::get(user_to_calculate.clone()); - - let key = SumTreeName::PositiveExternality { - user_address: user_to_calculate.clone(), - block_number: pe_block_number.clone(), - }; - - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::get_incentives_score_schelling_helper_link( - key.clone(), - phase_data, - RangePoint::ZeroToFive, - )?; - - let score = T::SchellingGameSharedSource::get_mean_value_link(key.clone()); - // println!("Score {:?}", score); - T::SharedStorageSource::set_positive_externality_link(user_to_calculate, score)?; - - Ok(()) - } - } -} diff --git a/pallets/positive-externality/src/mock.rs b/pallets/positive-externality/src/mock.rs deleted file mode 100644 index 2e056b2..0000000 --- a/pallets/positive-externality/src/mock.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate as pallet_template; -use frame_support::{ - parameter_types, - traits::{ConstU16, ConstU64, GenesisBuild}, -}; -use frame_support_test::TestRandomness; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - TemplateModule: pallet_template, - Balances: pallet_balances, - Timestamp: pallet_timestamp, - SharedStorage:shared_storage, - SchellingGameShared: schelling_game_shared, - SortitionSumGame: sortition_sum_game, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - type AccountData = pallet_balances::AccountData; // New code -} - -impl shared_storage::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} -parameter_types! { - pub const MinimumPeriod: u64 = 5; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU64<1>; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type MaxHolds = (); - type HoldIdentifier = (); - type AccountStore = System; -} - -impl schelling_game_shared::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Currency = Balances; // New code - type RandomnessSource = TestRandomness; - type Slash = (); - type Reward = (); - type SortitionSumGameSource = SortitionSumGame; -} - -impl sortition_sum_game::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type SharedStorageSource = SharedStorage; - type Currency = Balances; // New code - type SchellingGameSharedSource = SchellingGameShared; -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 100000), - (2, 200000), - (3, 300000), - (4, 300000), - (5, 300000), - (6, 300000), - (7, 300000), - (8, 300000), - (9, 300000), - (10, 300000), - (11, 300000), - (12, 300000), - (13, 300000), - (14, 300000), - (15, 300000), - (16, 300000), - (17, 300000), - (18, 300000), - (19, 300000), - (20, 300000), - (21, 300000), - (22, 300000), - (23, 300000), - (24, 300000), - (25, 300000), - (26, 300000), - (27, 300000), - (28, 300000), - (29, 300000), - (30, 300000), - (31, 300000), - (32, 300000), - (33, 300000), - (34, 300000), - (35, 300000), - ], - } // new code - .assimilate_storage(&mut t) - .unwrap(); - shared_storage::GenesisConfig:: { approved_citizen_address: vec![1, 2] } - .assimilate_storage(&mut t) - .unwrap(); - t.into() -} diff --git a/pallets/positive-externality/src/tests.rs b/pallets/positive-externality/src/tests.rs deleted file mode 100644 index 8421940..0000000 --- a/pallets/positive-externality/src/tests.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::types::Post; -use crate::{mock::*, Error, Event}; -use frame_support::{assert_noop, assert_ok}; -use pallet_support::{Content, WhoAndWhen}; - -#[test] -fn test_positive_externality_post() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::create_positive_externality_post( - RuntimeOrigin::signed(1), - Content::None - )); - let post = TemplateModule::post_by_id(1); - let post_compare = Some(Post { - id: 1, - created: WhoAndWhen { account: 1, block: 0, time: 0 }, - edited: false, - owner: 1, - content: Content::None, - hidden: false, - upvotes_count: 0, - downvotes_count: 0, - }); - assert_eq!(post, post_compare); - // assert_ok!(TemplateModule::apply_jurors(Origin::signed(1), 2, 60)); - }); -} - -#[test] -fn test_setting_positive_externality_validation() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::set_validate_positive_externality( - RuntimeOrigin::signed(1), - true - )); - let value = TemplateModule::validate(1); - assert_eq!(value, true); - }); -} - -#[test] -fn test_applying_for_staking_period() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::set_validate_positive_externality( - RuntimeOrigin::signed(1), - true - )); - System::set_block_number(1298000); - assert_ok!(TemplateModule::apply_staking_period(RuntimeOrigin::signed(2), 1)); - System::set_block_number(1298000 + 1298000); - assert_ok!(TemplateModule::apply_staking_period(RuntimeOrigin::signed(2), 1)); - }); -} - -#[test] -fn test_appying_jurors() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::set_validate_positive_externality( - RuntimeOrigin::signed(1), - true - )); - // System::set_block_number(1298000); - assert_ok!(TemplateModule::apply_staking_period(RuntimeOrigin::signed(2), 1)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(4), 1, 1000)); - }); -} - -#[test] -fn test_change_period() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::set_validate_positive_externality( - RuntimeOrigin::signed(1), - true - )); - System::set_block_number(1298000); - assert_ok!(TemplateModule::apply_staking_period(RuntimeOrigin::signed(2), 1)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(4), 1, 1000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(5), 1, 2000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(6), 1, 3000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(7), 1, 4000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(8), 1, 5000)); - System::set_block_number(1298080); - assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); - }) -} - -#[test] -fn test_draw_jurors_period() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::set_validate_positive_externality( - RuntimeOrigin::signed(1), - true - )); - System::set_block_number(1298000); - assert_ok!(TemplateModule::apply_staking_period(RuntimeOrigin::signed(2), 1)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(4), 1, 1000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(5), 1, 2000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(6), 1, 3000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(7), 1, 4000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(8), 1, 5000)); - System::set_block_number(1298080); - assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); - assert_ok!(TemplateModule::draw_jurors(RuntimeOrigin::signed(8), 1, 5)); - }) -} - -#[test] -fn test_drawn_jurors() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::set_validate_positive_externality( - RuntimeOrigin::signed(1), - true - )); - System::set_block_number(1298000); - assert_ok!(TemplateModule::apply_staking_period(RuntimeOrigin::signed(2), 1)); - let balance = Balances::free_balance(4); - assert_eq!(300000, balance); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(4), 1, 1000)); - let balance = Balances::free_balance(4); - assert_eq!(299000, balance); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(5), 1, 2000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(6), 1, 3000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(7), 1, 4000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(8), 1, 5000)); - System::set_block_number(1298080); - assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); - assert_ok!(TemplateModule::draw_jurors(RuntimeOrigin::signed(8), 1, 5)); - let data = TemplateModule::get_drawn_jurors(1); - assert_eq!(data, [(4, 1000), (5, 2000), (6, 3000), (7, 4000), (8, 5000)]); - // println!("drawn jurors {:?}",data); - }) -} - -#[test] -fn test_commit_and_incentives_vote() { - new_test_ext().execute_with(|| { - assert_ok!(TemplateModule::set_validate_positive_externality( - RuntimeOrigin::signed(1), - true - )); - System::set_block_number(1298000); - assert_ok!(TemplateModule::apply_staking_period(RuntimeOrigin::signed(2), 1)); - let balance = Balances::free_balance(4); - assert_eq!(300000, balance); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(4), 1, 1000)); - let balance = Balances::free_balance(4); - assert_eq!(299000, balance); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(5), 1, 2000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(6), 1, 3000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(7), 1, 4000)); - assert_ok!(TemplateModule::apply_jurors(RuntimeOrigin::signed(8), 1, 5000)); - System::set_block_number(1298080); - assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); - assert_ok!(TemplateModule::draw_jurors(RuntimeOrigin::signed(8), 1, 5)); - - let data = TemplateModule::get_drawn_jurors(1); - assert_eq!(data, [(4, 1000), (5, 2000), (6, 3000), (7, 4000), (8, 5000)]); - assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); - - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_ok!(TemplateModule::commit_vote(RuntimeOrigin::signed(4), 1, hash)); - - let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); - assert_ok!(TemplateModule::commit_vote(RuntimeOrigin::signed(5), 1, hash)); - let hash = sp_io::hashing::keccak_256("5salt3".as_bytes()); - assert_ok!(TemplateModule::commit_vote(RuntimeOrigin::signed(6), 1, hash)); - let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); - assert_ok!(TemplateModule::commit_vote(RuntimeOrigin::signed(7), 1, hash)); - let hash = sp_io::hashing::keccak_256("5salt5".as_bytes()); - assert_ok!(TemplateModule::commit_vote(RuntimeOrigin::signed(8), 1, hash)); - System::set_block_number(12980160); - assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); - assert_ok!(TemplateModule::reveal_vote( - RuntimeOrigin::signed(4), - 1, - 1, - "salt".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote( - RuntimeOrigin::signed(5), - 1, - 1, - "salt2".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote( - RuntimeOrigin::signed(6), - 1, - 5, - "salt3".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote( - RuntimeOrigin::signed(7), - 1, - 1, - "salt4".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote( - RuntimeOrigin::signed(8), - 1, - 5, - "salt5".as_bytes().to_vec() - )); - System::set_block_number(12980260); - assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1)); - - assert_ok!(TemplateModule::get_incentives(RuntimeOrigin::signed(4), 1)); - }) -} diff --git a/pallets/posts/Cargo.toml b/pallets/posts/Cargo.toml deleted file mode 100644 index dc0172b..0000000 --- a/pallets/posts/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "pallet-posts" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -# polars = {version = "0.36.2", default-features = false} - -# Substrate dependencies -pallet-support = { default-features = false, path = '../support' } -pallet-timestamp = {git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42", default-features = false } -pallet-spaces = {default-features = false, path = '../spaces' } - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "pallet-timestamp/std", - "pallet-support/std", - "pallet-spaces/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/posts/src/benchmarking.rs b/pallets/posts/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/posts/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/posts/src/extras.rs b/pallets/posts/src/extras.rs deleted file mode 100644 index f38657e..0000000 --- a/pallets/posts/src/extras.rs +++ /dev/null @@ -1,3 +0,0 @@ -use crate::*; - -impl Pallet {} diff --git a/pallets/posts/src/functions.rs b/pallets/posts/src/functions.rs deleted file mode 100644 index 620ec7b..0000000 --- a/pallets/posts/src/functions.rs +++ /dev/null @@ -1,139 +0,0 @@ -use frame_support::dispatch::DispatchResult; -use sp_runtime::traits::Saturating; - -use pallet_support::{remove_from_vec, SpaceId}; - -use super::*; - -impl Post { - pub fn new( - id: PostId, - created_by: T::AccountId, - space_id_opt: Option, - extension: PostExtension, - content: Content, - ) -> Self { - Post { - id, - created: new_who_and_when::(created_by.clone()), - edited: false, - owner: created_by, - extension, - space_id: space_id_opt, - content, - hidden: false, - upvotes_count: 0, - downvotes_count: 0, - } - } - - pub fn ensure_owner(&self, account: &T::AccountId) -> DispatchResult { - ensure!(self.is_owner(account), Error::::NotAPostOwner); - Ok(()) - } - - pub fn is_owner(&self, account: &T::AccountId) -> bool { - self.owner == *account - } - - pub fn is_root_post(&self) -> bool { - !self.is_comment() - } - - pub fn is_regular_post(&self) -> bool { - matches!(self.extension, PostExtension::RegularPost) - } - - pub fn is_comment(&self) -> bool { - matches!(self.extension, PostExtension::Comment(_)) - } - - pub fn is_shared_post(&self) -> bool { - matches!(self.extension, PostExtension::SharedPost(_)) - } - - pub fn get_comment_ext(&self) -> Result { - match self.extension { - PostExtension::Comment(comment_ext) => Ok(comment_ext), - _ => Err(Error::::NotComment.into()), - } - } - - pub fn get_original_post_id(&self) -> Result { - match self.extension { - PostExtension::SharedPost(original_post_id) => Ok(original_post_id), - _ => Err(Error::::NotASharedPost.into()), - } - } - - pub fn get_root_post(&self) -> Result, DispatchError> { - match self.extension { - PostExtension::RegularPost | PostExtension::SharedPost(_) => Ok(self.clone()), - PostExtension::Comment(comment) => Pallet::::require_post(comment.root_post_id), - } - } - - pub fn get_space_id(&self) -> Result { - Self::try_get_space_id(self).ok_or_else(|| Error::::PostHasNoSpaceId.into()) - } - - pub fn try_get_space_id(&self) -> Option { - if let Ok(root_post) = self.get_root_post() { - return root_post.space_id; - } - - None - } - - pub fn get_space(&self) -> Result, DispatchError> { - let root_post = self.get_root_post()?; - let space_id = root_post.space_id.ok_or(Error::::PostHasNoSpaceId)?; - Spaces::require_space(space_id) - } - - pub fn try_get_space(&self) -> Option> { - if let Ok(root_post) = self.get_root_post() { - return root_post.space_id.and_then(|space_id| Spaces::require_space(space_id).ok()); - } - - None - } - - pub fn try_get_parent_id(&self) -> Option { - match self.extension { - PostExtension::Comment(comment_ext) => comment_ext.parent_id, - _ => None, - } - } - - pub fn inc_upvotes(&mut self) { - self.upvotes_count.saturating_inc(); - } - - pub fn dec_upvotes(&mut self) { - self.upvotes_count.saturating_dec(); - } - - pub fn inc_downvotes(&mut self) { - self.downvotes_count.saturating_inc(); - } - - pub fn dec_downvotes(&mut self) { - self.downvotes_count.saturating_dec(); - } - - pub fn is_public(&self) -> bool { - !self.hidden && self.content.is_some() - } - - pub fn is_unlisted(&self) -> bool { - !self.is_public() - } -} - -impl Pallet { - /// Get `Post` by id from the storage or return `PostNotFound` error. - pub fn require_post(post_id: SpaceId) -> Result, DispatchError> { - Ok(Self::post_by_id(post_id).ok_or(Error::::PostNotFound)?) - } -} diff --git a/pallets/posts/src/lib.rs b/pallets/posts/src/lib.rs deleted file mode 100644 index 6a3d68e..0000000 --- a/pallets/posts/src/lib.rs +++ /dev/null @@ -1,268 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -mod extras; -pub mod functions; -pub mod types; - -pub use types::{Comment, Post, PostExtension, PostUpdate, FIRST_POST_ID}; - -use codec::{Decode, Encode}; - -use frame_support::pallet_prelude::*; -use frame_support::sp_std::prelude::*; -use frame_system::pallet_prelude::*; -use pallet_spaces::{types::Space, Pallet as Spaces}; -use pallet_support::{ - ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, PostId, SpaceId, - WhoAndWhen, WhoAndWhenOf, -}; - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_timestamp::Config + pallet_spaces::Config - { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::type_value] - pub fn DefaultForNextPostId() -> PostId { - FIRST_POST_ID - } - - /// The next post id. - #[pallet::storage] - #[pallet::getter(fn next_post_id)] - pub type NextPostId = StorageValue<_, PostId, ValueQuery, DefaultForNextPostId>; - - /// Get the details of a post by its' id. - #[pallet::storage] - #[pallet::getter(fn post_by_id)] - pub type PostById = StorageMap<_, Twox64Concat, PostId, Post>; - - /// Get the ids of all direct replies by their parent's post id. - #[pallet::storage] - #[pallet::getter(fn reply_ids_by_post_id)] - pub type ReplyIdsByPostId = - StorageMap<_, Twox64Concat, PostId, Vec, ValueQuery>; - - /// Get the ids of all posts in a given space, by the space's id. - #[pallet::storage] - #[pallet::getter(fn post_ids_by_space_id)] - pub type PostIdsBySpaceId = - StorageMap<_, Twox64Concat, SpaceId, Vec, ValueQuery>; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { - something: u32, - who: T::AccountId, - }, - PostCreated { - account: T::AccountId, - post_id: PostId, - }, - PostUpdated { - account: T::AccountId, - post_id: PostId, - }, - PostMoved { - account: T::AccountId, - post_id: PostId, - from_space: Option, - to_space: Option, - }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - // Post related errors: - /// Post was not found by id. - PostNotFound, - /// An account is not a post owner. - NotAPostOwner, - /// Nothing to update in this post. - NoUpdatesForPost, - /// Root post should have a space id. - PostHasNoSpaceId, - /// Not allowed to create a post/comment when a scope (space or root post) is hidden. - CannotCreateInHiddenScope, - /// Post has no replies. - NoRepliesOnPost, - /// Cannot move a post to the same space. - CannotMoveToSameSpace, - - // Share related errors: - /// Cannot share, because the original post was not found. - OriginalPostNotFound, - /// Cannot share a post that is sharing another post. - CannotShareSharedPost, - /// This post's extension is not a `SharedPost`. - NotASharedPost, - - // Comment related errors: - /// Unknown parent comment id. - UnknownParentComment, - /// Post by `parent_id` is not of a `Comment` extension. - NotACommentByParentId, - /// Cannot update space id of a comment. - CannotUpdateSpaceIdOnComment, - /// Max comment depth reached. - MaxCommentDepthReached, - /// Only comment owner can update this comment. - NotACommentAuthor, - /// This post's extension is not a `Comment`. - NotComment, - - // Permissions related errors: - /// User has no permission to create root posts in this space. - NoPermissionToCreatePosts, - /// User has no permission to create comments (aka replies) in this space. - NoPermissionToCreateComments, - /// User has no permission to share posts/comments from this space to another space. - NoPermissionToShare, - /// User has no permission to update any posts in this space. - NoPermissionToUpdateAnyPost, - /// A post owner is not allowed to update their own posts in this space. - NoPermissionToUpdateOwnPosts, - /// A comment owner is not allowed to update their own comments in this space. - NoPermissionToUpdateOwnComments, - - /// `force_create_post` failed, because this post already exists. - /// Consider removing the post with `force_remove_post` first. - PostAlreadyExists, - } - - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. - #[pallet::call] - impl Pallet { - /// Create post - /// Who can post, does kyc validation required?? - #[pallet::call_index(0)] - #[pallet::weight(0)] - pub fn create_post( - origin: OriginFor, - space_id_opt: Option, - extension: PostExtension, - content: Content, - ) -> DispatchResult { - let creator = ensure_signed(origin)?; - - ensure_content_is_valid(content.clone())?; - - let new_post_id = Self::next_post_id(); - - let new_post: Post = - Post::new(new_post_id, creator.clone(), space_id_opt, extension, content.clone()); - - // Get space from either space_id_opt or Comment if a comment provided - let space = &new_post.get_space()?; - if new_post.is_root_post() { - PostIdsBySpaceId::::mutate(space.id, |ids| ids.push(new_post_id)); - } - - PostById::insert(new_post_id, new_post); - NextPostId::::mutate(|n| { - *n += 1; - }); - - Self::deposit_event(Event::PostCreated { account: creator, post_id: new_post_id }); - - Ok(()) - } - - #[pallet::call_index(1)] - #[pallet::weight(0)] - pub fn update_post( - origin: OriginFor, - post_id: PostId, - update: PostUpdate, - ) -> DispatchResult { - let editor = ensure_signed(origin)?; - - let has_updates = update.content.is_some() || update.hidden.is_some(); - - ensure!(has_updates, Error::::NoUpdatesForPost); - - let mut post = Self::require_post(post_id)?; - - let space_opt = &post.try_get_space(); - - let mut is_update_applied = false; - - if let Some(content) = update.content { - if content != post.content { - ensure_content_is_valid(content.clone())?; - - post.content = content; - post.edited = true; - is_update_applied = true; - } - } - - if let Some(hidden) = update.hidden { - if hidden != post.hidden { - post.hidden = hidden; - is_update_applied = true; - } - } - - // Update this post only if at least one field should be updated: - if is_update_applied { - >::insert(post.id, post); - Self::deposit_event(Event::PostUpdated { account: editor, post_id }); - } - - Ok(()) - } - } -} diff --git a/pallets/posts/src/mock.rs b/pallets/posts/src/mock.rs deleted file mode 100644 index 648822d..0000000 --- a/pallets/posts/src/mock.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate as pallet_template; -use frame_support::{ - parameter_types, - traits::{ConstU16, ConstU64, GenesisBuild}, -}; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - TemplateModule: pallet_template, - Spaces: pallet_spaces, - Timestamp: pallet_timestamp, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -impl pallet_spaces::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -parameter_types! { - pub const MinimumPeriod: u64 = 5; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() -} diff --git a/pallets/posts/src/tests.rs b/pallets/posts/src/tests.rs deleted file mode 100644 index d07fba9..0000000 --- a/pallets/posts/src/tests.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::{mock::*, Error, Event}; -use frame_support::{assert_noop, assert_ok}; - -#[test] -fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - }); -} - -#[test] -fn correct_error_for_none_value() { - new_test_ext().execute_with(|| {}); -} diff --git a/pallets/posts/src/types.rs b/pallets/posts/src/types.rs deleted file mode 100644 index 2f3b410..0000000 --- a/pallets/posts/src/types.rs +++ /dev/null @@ -1,68 +0,0 @@ -use super::*; - -pub const FIRST_POST_ID: u64 = 1; - -/// Information about a post's owner, its' related space, content, and visibility. -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct Post { - /// Unique sequential identifier of a post. Examples of post ids: `1`, `2`, `3`, and so on. - pub id: PostId, - - pub created: WhoAndWhenOf, - /// True, if the content of this post was edited. - pub edited: bool, - - /// The current owner of a given post. - pub owner: T::AccountId, - - /// Through post extension you can provide specific information necessary for different kinds - /// of posts such as regular posts, comments, and shared posts. - pub extension: PostExtension, - - /// An id of a space which contains a given post. - pub space_id: Option, - - pub content: Content, - - /// Hidden field is used to recommend to end clients (web and mobile apps) that a particular - /// posts and its' comments should not be shown. - pub hidden: bool, - - /// The number of times a given post has been upvoted. - pub upvotes_count: u32, - - /// The number of times a given post has been downvoted. - pub downvotes_count: u32, -} - -#[derive(Encode, Decode, Default, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct PostUpdate { - /// Deprecated: This field has no effect in `fn update_post()` extrinsic. - /// See `fn move_post()` extrinsic if you want to move a post to another space. - pub space_id: Option, - - pub content: Option, - pub hidden: Option, -} - -/// Post extension provides specific information necessary for different kinds -/// of posts such as regular posts, comments, and shared posts. -#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub enum PostExtension { - RegularPost, - Comment(Comment), - SharedPost(PostId), -} - -#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct Comment { - pub root_post_id: PostId, - pub parent_id: Option, -} - -impl Default for PostExtension { - fn default() -> Self { - PostExtension::RegularPost - } -} diff --git a/pallets/profile-validation/Cargo.toml b/pallets/profile-validation/Cargo.toml deleted file mode 100644 index 3015e0b..0000000 --- a/pallets/profile-validation/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -[package] -name = "profile-validation" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-timestamp = { git = 'https://github.com/paritytech/substrate', branch = "polkadot-v0.9.42", default-features = false } -pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-support = { default-features = false, path = '../support' } -schelling-game-shared = {default-features = false, path = "../schelling-game-shared"} -schelling-game-shared-link = {default-features = false, path = "../../traits/schelling-game-shared-link"} -sortition-sum-game = {default-features = false, path="../sortition-sum-game"} - - - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support-test = { version = "3.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "pallet-timestamp/std", - "pallet-balances/std", - "pallet-support/std", - "schelling-game-shared/std", - "sortition-sum-game/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/profile-validation/profile-validation-rpc/Cargo.toml b/pallets/profile-validation/profile-validation-rpc/Cargo.toml deleted file mode 100644 index 1e7e27d..0000000 --- a/pallets/profile-validation/profile-validation-rpc/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "profile-validation-rpc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } -sc-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-api = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -profile-validation-runtime-api = { default-features= false, path="../profile-validation-runtime-api"} \ No newline at end of file diff --git a/pallets/profile-validation/profile-validation-runtime-api/Cargo.toml b/pallets/profile-validation/profile-validation-runtime-api/Cargo.toml deleted file mode 100644 index 5abf0c2..0000000 --- a/pallets/profile-validation/profile-validation-runtime-api/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "profile-validation-runtime-api" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -sp-api = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42"} - -[features] -default = ["std"] -std = [ - "sp-api/std", - "frame-support/std", -] \ No newline at end of file diff --git a/pallets/profile-validation/profile-validation-runtime-api/src/lib.rs b/pallets/profile-validation/profile-validation-runtime-api/src/lib.rs deleted file mode 100644 index 47fa5f9..0000000 --- a/pallets/profile-validation/profile-validation-runtime-api/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -// use frame_support::sp_std::{vec::Vec}; -// or -use frame_support::sp_std::prelude::*; -use sp_api::codec::Codec; - -type ChallengePostId = u64; - -sp_api::decl_runtime_apis! { - pub trait ProfileValidationApi where AccountId: Codec { - - fn get_challengers_evidence(profile_user_account: AccountId, offset: u64, limit: u16) -> Vec; - - fn get_evidence_period_end_block(profile_user_account: AccountId) -> Option; - fn get_staking_period_end_block(profile_user_account: AccountId) -> Option; - fn get_drawing_period_end(profile_user_account: AccountId) -> (u64, u64, bool); - fn get_commit_period_end_block(profile_user_account: AccountId) -> Option; - fn get_vote_period_end_block(profile_user_account: AccountId) -> Option; - fn selected_as_juror(profile_user_account: AccountId, who: AccountId) -> bool; - } -} diff --git a/pallets/profile-validation/src/benchmarking.rs b/pallets/profile-validation/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/profile-validation/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/profile-validation/src/extras.rs b/pallets/profile-validation/src/extras.rs deleted file mode 100644 index f1be13b..0000000 --- a/pallets/profile-validation/src/extras.rs +++ /dev/null @@ -1,209 +0,0 @@ -use crate::*; - -impl CitizenDetailsPost { - pub fn new(citizen_id: CitizenId, created_by: T::AccountId, content: Content) -> Self { - CitizenDetailsPost { - created: new_who_and_when::(created_by.clone()), - content, - citizen_id, - owner: created_by, - edited: false, - hidden: false, - upvotes_count: 0, - downvotes_count: 0, - } - } - - pub fn ensure_owner(&self, account: &T::AccountId) -> DispatchResult { - ensure!(self.is_owner(account), Error::::NotAPostOwner); - Ok(()) - } - - pub fn is_owner(&self, account: &T::AccountId) -> bool { - self.owner == *account - } -} - -impl ChallengeEvidencePost { - pub fn new( - kyc_profile_id: T::AccountId, - created_by: T::AccountId, - content: Content, - post_id_if_comment: Option, - ) -> Self { - ChallengeEvidencePost { - created: new_who_and_when::(created_by.clone()), - owner: created_by, - kyc_profile_id, - content, - post_id_if_comment, - is_comment: false, - } - } - - pub fn ensure_owner(&self, account: &T::AccountId) -> DispatchResult { - ensure!(self.is_owner(account), Error::::NotAPostOwner); - Ok(()) - } - - pub fn is_owner(&self, account: &T::AccountId) -> bool { - self.owner == *account - } -} - -impl Pallet { - pub(super) fn get_phase_data() -> PhaseData { - T::SchellingGameSharedSource::create_phase_with_all_data( - 10, - 100, - 100, - 100, - 100, - 100, - 100, - 5, - 5, - 100, - (100, 100), - ) - // T::SchellingGameSharedSource::create_phase_data(100, 5, 3, 100, (100, 100)) - } - - // pub(super) fn get_citizen_accountid( - // citizenid: CitizenId, - // ) -> Result { - // let profile = Self::citizen_profile(citizenid).ok_or(Error::::CitizenDoNotExists)?; - // Ok(profile.owner) - // } - - pub(super) fn fund_profile_account() -> T::AccountId { - PALLET_ID.into_sub_account_truncating(1) - } - - pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { - input.saturated_into::>() - } - - pub(super) fn balance_to_u64_saturated(input: BalanceOf) -> u64 { - input.saturated_into::() - } - - pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { - input.saturated_into::>() - } - - pub fn get_challengers_evidence( - profile_user_account: T::AccountId, - offset: u64, - limit: u16, - ) -> Vec { - let mut data = >::iter_prefix_values(&profile_user_account) - .skip(offset as usize) - .take(limit as usize) - .collect::>(); - data.sort(); - data.reverse(); - data - } - - pub fn get_evidence_period_end_block(profile_user_account: T::AccountId) -> Option { - let now = >::block_number(); - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_staking_period_end_block(profile_user_account: T::AccountId) -> Option { - let now = >::block_number(); - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_drawing_period_end(profile_user_account: T::AccountId) -> (u64, u64, bool) { - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - let phase_data = Self::get_phase_data(); - - let result = - T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); - result - } - - pub fn get_commit_period_end_block(profile_user_account: T::AccountId) -> Option { - let now = >::block_number(); - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_vote_period_end_block(profile_user_account: T::AccountId) -> Option { - let now = >::block_number(); - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn selected_as_juror(profile_user_account: T::AccountId, who: T::AccountId) -> bool { - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - - let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); - result - } - - pub fn profile_fund_required(profile_user_account: T::AccountId) -> Option { - let registration_fee = Self::profile_registration_challenge_fees(); - let total_funded = Self::total_fund_for_profile_collected(profile_user_account); - let registration_fee_u64 = Self::balance_to_u64_saturated(registration_fee); - let total_fund_u64 = Self::balance_to_u64_saturated(total_funded); - let fund_required = registration_fee_u64.checked_sub(total_fund_u64); - fund_required - } -} diff --git a/pallets/profile-validation/src/lib.rs b/pallets/profile-validation/src/lib.rs deleted file mode 100644 index 1758862..0000000 --- a/pallets/profile-validation/src/lib.rs +++ /dev/null @@ -1,900 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -/// -/// To Do: -/// Add profile ✅ -/// Crowdfund for profile stake ✅ -/// Add another account in case you loose account access -/// Appeal in case of fradulent account -/// Clean the storage after are incentives are given -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -mod extras; -mod permissions; -mod types; - -use crate::types::{ChallengeEvidencePost, ChallengerFundInfo, ProfileFundInfo}; -use frame_support::sp_runtime::traits::AccountIdConversion; -use frame_support::sp_runtime::traits::{CheckedAdd, CheckedSub}; -use frame_support::sp_runtime::SaturatedConversion; -use frame_support::sp_std::prelude::*; -use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; -use frame_support::{ - traits::{ - Currency, ExistenceRequirement, Get, OnUnbalanced, ReservableCurrency, WithdrawReasons, - }, - PalletId, -}; - -use pallet_support::{ - ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, WhoAndWhen, WhoAndWhenOf, -}; -use schelling_game_shared::types::{ - Period, PhaseData, RangePoint, SchellingGameType, WinningDecision, -}; -use schelling_game_shared_link::SchellingGameSharedLink; -use sortition_sum_game::types::SumTreeName; -pub use types::{CitizenDetailsPost, FIRST_CHALLENGE_POST_ID, FIRST_CITIZEN_ID}; -type AccountIdOf = ::AccountId; -type BalanceOf = <::Currency as Currency>>::Balance; -type ProfileFundInfoOf = ProfileFundInfo, AccountIdOf>; -type ChallengerFundInfoOf = - ChallengerFundInfo, ::BlockNumber, AccountIdOf>; -pub type BlockNumberOf = ::BlockNumber; -type CitizenId = u64; -type ChallengePostId = u64; -type PositiveImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::PositiveImbalance; -type NegativeImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::NegativeImbalance; - -const PALLET_ID: PalletId = PalletId(*b"ex/cfund"); - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_timestamp::Config + schelling_game_shared::Config - { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - - type SchellingGameSharedSource: SchellingGameSharedLink< - SumTreeName = SumTreeName, - SchellingGameType = SchellingGameType, - BlockNumber = Self::BlockNumber, - AccountId = AccountIdOf, - Balance = BalanceOf, - RangePoint = RangePoint, - Period = Period, - WinningDecision = WinningDecision, - PhaseData = PhaseData, - >; - type Currency: ReservableCurrency; - /// Handler for the unbalanced increment when rewarding (minting rewards) - type Reward: OnUnbalanced>; - - /// Handler for the unbalanced decrement when slashing (burning collateral) - type Slash: OnUnbalanced>; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::type_value] - pub fn DefaultForNextCitizenId() -> CitizenId { - FIRST_CITIZEN_ID - } - - #[pallet::storage] - #[pallet::getter(fn next_citizen_id)] - pub type NextCitizenId = - StorageValue<_, CitizenId, ValueQuery, DefaultForNextCitizenId>; - - #[pallet::storage] - #[pallet::getter(fn get_citizen_id)] - pub type GetCitizenId = StorageMap<_, Blake2_128Concat, T::AccountId, CitizenId>; - - #[pallet::storage] - #[pallet::getter(fn citizen_profile)] - pub type CitizenProfile = - StorageMap<_, Blake2_128Concat, T::AccountId, CitizenDetailsPost>; // Peer account id => Peer Profile Hash - - // Registration Fees - - #[pallet::type_value] - pub fn DefaultRegistrationFee() -> BalanceOf { - 1000u128.saturated_into::>() - } - // Registration challenge fees - #[pallet::type_value] - pub fn DefaultRegistrationChallengeFee() -> BalanceOf { - 100u128.saturated_into::>() - } - - #[pallet::storage] - #[pallet::getter(fn profile_registration_fees)] - pub type RegistrationFee = - StorageValue<_, BalanceOf, ValueQuery, DefaultRegistrationFee>; - - #[pallet::storage] - #[pallet::getter(fn profile_registration_challenge_fees)] - pub type RegistrationChallengeFee = - StorageValue<_, BalanceOf, ValueQuery, DefaultRegistrationChallengeFee>; - - #[pallet::storage] - #[pallet::getter(fn profile_fund_details)] - pub type ProfileFundDetails = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, - Blake2_128Concat, - T::AccountId, - ProfileFundInfoOf, - >; // Profile account id and (funder accountid, profile fund info) - - #[pallet::storage] - #[pallet::getter(fn total_fund_for_profile_collected)] - pub type ProfileTotalFundCollected = - StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn validation_block)] - pub type ValidationBlock = - StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberOf, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn challenger_fund)] - pub type ChallengerFundDetails = - StorageMap<_, Blake2_128Concat, T::AccountId, ChallengerFundInfoOf>; // Profile account id and challenger fund info - - /// There is a single challenger, but they can have multiple posts - #[pallet::storage] - #[pallet::getter(fn challenger_evidence_query)] - pub type ChallengerEvidenceId = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, - Blake2_128Concat, - T::AccountId, - ChallengePostId, - >; // profile accountid, challenger accountid => Challenge post id - - #[pallet::type_value] - pub fn DefaultForNextChallengePostId() -> ChallengePostId { - FIRST_CHALLENGE_POST_ID - } - - #[pallet::storage] - #[pallet::getter(fn next_challenge_post_count)] - pub type NextChallengePostId = - StorageValue<_, ChallengePostId, ValueQuery, DefaultForNextChallengePostId>; - - #[pallet::storage] - #[pallet::getter(fn challenge_post_comment)] - pub type ChallengePostCommentIds = - StorageMap<_, Blake2_128Concat, ChallengePostId, Vec, ValueQuery>; // challenge post id => Vec - - #[pallet::storage] - #[pallet::getter(fn challenge_post)] - pub type ChallengePost = - StorageMap<_, Blake2_128Concat, ChallengePostId, ChallengeEvidencePost>; // challenge post id => post - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { - something: u32, - who: T::AccountId, - }, - CreateCitizen(T::AccountId, CitizenId), - ProfileFund { - profile: T::AccountId, - funder: T::AccountId, - }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - NoMoreUpdates, - CitizenDoNotExists, - ProfileFundExists, - PostAlreadyExists, - ProfileIsAlreadyValidated, - ChallengeExits, - ChallengeDoesNotExists, - CommentExists, - IsComment, - ProfileFundNotExists, - ChallengerFundInfoExists, - NotProfileUser, - NotEvidencePeriod, - CitizenNotApproved, - NotAPostOwner, - AmountFundedGreaterThanRequired, - ProfileFundAlreadyReturned, - } - - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. - #[pallet::call] - impl Pallet { - /// Add citizen - ///

-		/// Get the count from NextCitizenId
-		/// If CitizenId exists update the content, only if `ProfileTotalFundCollected` is zero
-		/// If CitizenId doesn't exists insert the content, and increment the `NextCitizenId`
-		/// 
- /// #[pallet::weight(::WeightInfo::add_citizen())] - #[pallet::call_index(0)] - #[pallet::weight(0)] - pub fn add_citizen(origin: OriginFor, content: Content) -> DispatchResult { - let who = ensure_signed(origin)?; - let count = Self::next_citizen_id(); - match >::get(&who) { - Some(citizen_id) => { - let total_funded = >::get(who.clone()); - if total_funded == 0u128.saturated_into::>() { - let new_post: CitizenDetailsPost = - CitizenDetailsPost::new(citizen_id, who.clone(), content.clone()); - >::insert(who.clone(), new_post); - Ok(()) - } else { - Err(Error::::NoMoreUpdates)? - } - }, - None => { - >::insert(&who, count); - - let new_post: CitizenDetailsPost = - CitizenDetailsPost::new(count, who.clone(), content.clone()); - - >::insert(who.clone(), new_post); - NextCitizenId::::mutate(|n| { - *n += 1; - }); - Self::deposit_event(Event::CreateCitizen(who, count)); - Ok(()) - }, - } - } - - /// # Crowdfunding of Profile - /// - /// Allows users to contribute funds to a profile and manages the associated data for crowdfunding. - /// - /// ## Parameters - /// - /// - `origin`: The origin of the transaction. - /// - `profile_user_account`: The account ID of the profile to fund. - /// - `amount_to_fund`: The amount of funds to be added to the profile's crowdfunding. - /// - /// ## Errors - /// - /// This function can return an error if the amount to fund is greater than the required fund. - /// - /// ## Storage - /// - /// - `ValidationBlock`: Stores the block number to be used for the profile's validation when `amount_to_fund` equals `required_fund`. - /// - `ProfileFundDetails`: Stores details of funds deposited by users for a specific profile. - /// - `ProfileTotalFundCollected`: Keeps track of the total funds collected for each profile. - /// - `RegistrationFee`: Retrieves the registration fee required for profile validation. - /// - `GetCitizenId`: Storage map that associates a citizen's account address with their Citizen ID. - /// - /// ## Usage - /// - /// Call this function to contribute funds to a profile and update the associated storage items. - /// Checks are performed to ensure the profile exists and that the funded amount is not greater - /// than required. If the funded amount matches the required amount, the profile validation is marked - /// as completed, and a link is set to the evidence period in the Schelling Game. - /// - /// ```rust,ignore - /// #[pallet::call] - /// fn add_profile_stake( - /// origin: OriginFor, - /// profile_user_account: T::AccountId, - /// amount_to_fund: BalanceOf, - /// ) -> DispatchResult { - /// // implementation - /// } - /// ``` - - #[pallet::call_index(1)] - #[pallet::weight(0)] - pub fn add_profile_stake( - origin: OriginFor, - profile_user_account: T::AccountId, - amount_to_fund: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - // Ensure that the `profile_user_account`` exists in `GetCitizenId` storage. - Self::ensure_account_id_has_profile(profile_user_account.clone())?; - - // Retrieve the registration fee required for profile validation. - let registration_fee = >::get(); - - // Get the total funds already collected for the profile. - let total_funded = >::get(profile_user_account.clone()); - - // Calculate the required fund by subtracting the total funded from the registration fee. - let required_fund = registration_fee.checked_sub(&total_funded).expect("Overflow"); - - // Check if the amount_to_fund is less than or equal to the required fund. - if amount_to_fund <= required_fund { - if amount_to_fund == required_fund { - // If the funded amount matches the required amount, update variables required for profile validation. - let now = >::block_number(); - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number: now.clone(), - }; - >::insert(&profile_user_account, now); - - // Set a link to the evidence period in the Schelling Game. - T::SchellingGameSharedSource::set_to_evidence_period_link(key, now)?; - } - - // Withdraw funds from the funder's account. - let _ = ::Currency::withdraw( - &who, - amount_to_fund.clone(), - WithdrawReasons::TRANSFER, - ExistenceRequirement::AllowDeath, - )?; - - // Update the profile fund details for the funder. - match >::get(profile_user_account.clone(), who.clone()) { - Some(mut profile_fund_info) => { - let deposit = profile_fund_info.deposit; - let new_deposit = deposit.checked_add(&amount_to_fund).expect("Overflow"); - profile_fund_info.deposit = new_deposit; - >::insert( - profile_user_account.clone(), - who.clone(), - profile_fund_info, - ); - }, - None => { - let profile_fund_info = ProfileFundInfo { - funder_account_id: who.clone(), - validation_account_id: profile_user_account.clone(), - deposit: amount_to_fund.clone(), - deposit_returned: false, - }; - >::insert( - profile_user_account.clone(), - who.clone(), - profile_fund_info, - ); - }, - } - - // Update the total funds collected for the profile. - let next_total_fund = total_funded.checked_add(&amount_to_fund).expect("overflow"); - >::insert( - profile_user_account.clone(), - next_total_fund, - ); - - // Emit a ProfileFund event. - Self::deposit_event(Event::ProfileFund { - profile: profile_user_account, - funder: who, - }); - } else { - // Return an error if the funded amount is greater than required. - Err(Error::::AmountFundedGreaterThanRequired)? - } - - Ok(()) - } - - // Add fees for challenge profile ✔️ - #[pallet::call_index(2)] - #[pallet::weight(0)] - pub fn challenge_profile( - origin: OriginFor, - profile_user_account: T::AccountId, - content: Content, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - Self::ensure_account_id_has_profile(profile_user_account.clone())?; - let now = >::block_number(); - - let fees = Self::profile_registration_challenge_fees(); - - let challenger_fund_info = ChallengerFundInfo { - challengerid: who.clone(), - deposit: fees, - start: now.clone(), - challenge_completed: false, - }; - - let challenger_fund_details = >::get(&profile_user_account); - match challenger_fund_details { - Some(_value) => Err(Error::::ChallengeExits)?, - None => { - let _ = ::Currency::withdraw( - &who, - fees.clone(), - WithdrawReasons::TRANSFER, - ExistenceRequirement::AllowDeath, - )?; - >::insert(&profile_user_account, challenger_fund_info); - }, - } - - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::set_to_staking_period_link(key.clone(), phase_data, now)?; - T::SchellingGameSharedSource::create_tree_helper_link(key.clone(), 3)?; - - let count = Self::next_challenge_post_count(); - - let challenge_evidence_post: ChallengeEvidencePost = ChallengeEvidencePost::new( - profile_user_account.clone(), - who.clone(), - content, - None, - ); - - match >::get(&profile_user_account, &who) { - None => { - >::insert(&count, challenge_evidence_post); - NextChallengePostId::::mutate(|n| { - *n += 1; - }); - - >::insert(&profile_user_account, &who, count); - }, - Some(_hash) => Err(Error::::PostAlreadyExists)?, - } - Ok(()) - } - - // #[pallet::call_index(2)] - // #[pallet::weight(Weight::from_parts(10_000, u64::MAX) + T::DbWeight::get().reads_writes(2,2))] - // pub fn challenge_evidence( - // origin: OriginFor, - // profile_citizenid: CitizenId, - // content: Content, - // ) -> DispatchResult { - // let who = ensure_signed(origin)?; - // let citizen_account_id = Self::get_citizen_accountid(profile_citizenid)?; - // let count = Self::next_challenge_post_count(); - // let challenge_evidence_post = - // ChallengeEvidencePost::new(citizen_account_id, who.clone(), content, None); - // match >::get(&profile_citizenid, &who) { - // None => { - // >::insert(&count, challenge_evidence_post); - // NextChallengePostId::::mutate(|n| { - // *n += 1; - // }); - - // >::insert(&profile_citizenid, &who, count); - // }, - // Some(_hash) => { - // Err(Error::::PostAlreadyExists)? - // // match >::get(&profile_citizenid) { - // // Some(_challengerfundinfo) => { - // // Err(Error::::ChallengerFundAddedCanNotUpdate)? - // // }, - // // None => { - // // // Update challenger profile - // // >::insert(&count, challenge_evidence_post); - // // let newcount = - // // count.checked_add(1).ok_or(Error::::StorageOverflow)?; - // // >::put(newcount); - // // >::insert(&profile_citizenid, &who, count); - // // }, - // // } - // }, - // } - // Ok(()) - // } - - #[pallet::call_index(3)] - #[pallet::weight(0)] - pub fn challenge_comment_create( - origin: OriginFor, - post_id: ChallengePostId, - content: Content, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let count = Self::next_challenge_post_count(); - let main_evidence_post = Self::challenge_post(post_id).unwrap(); - let challenge_evidence_post = ChallengeEvidencePost::new( - main_evidence_post.kyc_profile_id, - who, - content, - Some(post_id), - ); - - match >::get(&post_id) { - None => Err(Error::::ChallengeDoesNotExists)?, - Some(challenge_evidence_post_c) => { - if challenge_evidence_post_c.is_comment == false { - >::insert(&count, challenge_evidence_post); - NextChallengePostId::::mutate(|n| { - *n += 1; - }); - let mut comment_ids = >::get(&post_id); - match comment_ids.binary_search(&count) { - Ok(_) => Err(Error::::CommentExists)?, - Err(index) => { - comment_ids.insert(index, count.clone()); - >::insert(&post_id, &comment_ids); - }, - } - } else { - Err(Error::::IsComment)? - } - }, - } - - Ok(()) - } - - // // Does citizen exists ✔️ - // // Has the citizen added profile fund ✔️ - // // Create tree ✔️ - // // Check evidence has been submitted - // #[pallet::call_index(4)] - // #[pallet::weight(Weight::from_parts(10_000, u64::MAX) + T::DbWeight::get().reads_writes(2,2))] - // pub fn challenge_profile( - // origin: OriginFor, - // profile_citizenid: CitizenId, - // ) -> DispatchResult { - // let who = ensure_signed(origin)?; - // let key = SumTreeName::UniqueIdenfier1 { - // citizen_id: profile_citizenid, - // name: "challengeprofile".as_bytes().to_vec(), - // }; - // let phase_data = Self::get_phase_data(); - // let now = >::block_number(); - // let _citizen_account_id = Self::get_citizen_accountid(profile_citizenid)?; - // match >::get(&profile_citizenid) { - // Some(profilefundinfo) => { - // if profilefundinfo.validated == true { - // Err(Error::::ProfileIsAlreadyValidated)?; - // } else { - // let _evidence_stake_block_number = profilefundinfo.start; // remove the profile fund info start - - // T::SchellingGameSharedSource::set_to_staking_period_link( - // key.clone(), - // phase_data, - // now, - // )?; - // } - // }, - // None => { - // Err(Error::::ProfileFundNotExists)?; - // }, - // } - // let deposit = >::get(); - // let imb = ::Currency::withdraw( - // &who, - // deposit, - // WithdrawReasons::TRANSFER, - // ExistenceRequirement::AllowDeath, - // )?; - - // ::Currency::resolve_creating(&Self::fund_profile_account(), imb); - - // match >::get(&profile_citizenid) { - // // 📝 To write update stake for reapply - // Some(_challengerfundinfo) => Err(Error::::ChallengerFundInfoExists)?, - // None => { - // let challenger_fund_info = ChallengerFundInfo { - // challengerid: who, - // deposit, - // start: now, - // challenge_completed: false, - // }; - // >::insert(&profile_citizenid, challenger_fund_info); - // }, - // } - // T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; - - // Ok(()) - // } - - // May be you need to check challeger fund details exists - #[pallet::call_index(5)] - #[pallet::weight(0)] - pub fn pass_period( - origin: OriginFor, - profile_user_account: T::AccountId, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - - let now = >::block_number(); - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; - - Ok(()) - } - - // To Do - // Apply jurors or stake ✔️ - // Update stake - // Draw jurors ✔️ - // Unstaking non selected jurors ✔️ - // Commit vote ✔️ - // Reveal vote ✔️ - // Get winning decision ✔️ - // Incentive distribution ✔️ - - // Staking - // 1. Check for minimum stake ✔️ - // 2. Check period is Staking ✔️ - // 3. Number of people staked - - #[pallet::call_index(6)] - #[pallet::weight(0)] - pub fn apply_jurors( - origin: OriginFor, - profile_user_account: T::AccountId, - stake: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::apply_jurors_helper_link(key, phase_data, who, stake)?; - - Ok(()) - } - - // Draw jurors - // Check period is drawing ✔️ - // Check mininum number of juror staked ✔️ - // Improvements - // Set stake to zero so that they are not drawn again - // Store the drawn juror stake in hashmap storage - // Add min draws along with max draws - #[pallet::call_index(7)] - #[pallet::weight(0)] - pub fn draw_jurors( - origin: OriginFor, - profile_user_account: T::AccountId, - iterations: u64, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; - - Ok(()) - } - - // Unstaking - // Stop drawn juror to unstake ✔️ - #[pallet::call_index(8)] - #[pallet::weight(0)] - pub fn unstaking( - origin: OriginFor, - profile_user_account: T::AccountId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; - Ok(()) - } - - #[pallet::call_index(9)] - #[pallet::weight(0)] - pub fn commit_vote( - origin: OriginFor, - profile_user_account: T::AccountId, - vote_commit: [u8; 32], - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - T::SchellingGameSharedSource::commit_vote_helper_link(key, who, vote_commit)?; - Ok(()) - } - - #[pallet::call_index(10)] - #[pallet::weight(0)] - pub fn reveal_vote( - origin: OriginFor, - profile_user_account: T::AccountId, - choice: u128, - salt: Vec, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - - T::SchellingGameSharedSource::reveal_vote_two_choice_helper_link( - key, who, choice, salt, - )?; - - Ok(()) - } - - #[pallet::call_index(11)] - #[pallet::weight(0)] - pub fn get_incentives( - origin: OriginFor, - profile_user_account: T::AccountId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = >::get(&profile_user_account); - - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( - key, phase_data, who, - )?; - Ok(()) - } - - #[pallet::call_index(12)] - #[pallet::weight(0)] - pub fn return_profile_stake( - origin: OriginFor, - profile_user_account: T::AccountId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = >::get(&profile_user_account); - let key = SumTreeName::ProfileValidation { - citizen_address: profile_user_account.clone(), - block_number, - }; - let now = >::block_number(); - let phase_data = Self::get_phase_data(); - - let period = T::SchellingGameSharedSource::get_period_link(key.clone()).unwrap(); - if period == Period::Execution { - let decision: WinningDecision = - T::SchellingGameSharedSource::get_winning_decision_value_link(key.clone()); - if decision == WinningDecision::WinnerNo { - match >::get(profile_user_account.clone(), who.clone()) { - Some(mut profile_fund_info) => { - if profile_fund_info.deposit_returned == false { - let r = ::Currency::deposit_into_existing( - &who, - profile_fund_info.deposit, - ) - .ok() - .unwrap(); - ::Reward::on_unbalanced(r); - profile_fund_info.deposit_returned = true; - >::insert( - profile_user_account.clone(), - who.clone(), - profile_fund_info, - ); - } else { - Err(Error::::ProfileFundAlreadyReturned)?; - } - }, - None => { - Err(Error::::ProfileFundNotExists)?; - }, - } - } - } else if period == Period::Evidence { - T::SchellingGameSharedSource::ensure_time_for_staking_over_link( - key, phase_data, now, - )?; - match >::get(profile_user_account.clone(), who.clone()) { - Some(mut profile_fund_info) => { - if profile_fund_info.deposit_returned == false { - let r = ::Currency::deposit_into_existing( - &who, - profile_fund_info.deposit, - ) - .ok() - .unwrap(); - ::Reward::on_unbalanced(r); - profile_fund_info.deposit_returned = true; - >::insert( - profile_user_account.clone(), - who.clone(), - profile_fund_info, - ); - } else { - Err(Error::::ProfileFundAlreadyReturned)?; - } - }, - None => { - Err(Error::::ProfileFundNotExists)?; - }, - } - } - - Ok(()) - } - } -} diff --git a/pallets/profile-validation/src/mock.rs b/pallets/profile-validation/src/mock.rs deleted file mode 100644 index c0b9332..0000000 --- a/pallets/profile-validation/src/mock.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate as pallet_template; -use frame_support::{ - parameter_types, - traits::{ConstU16, ConstU64}, -}; -use frame_support_test::TestRandomness; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - ProfileValidation: pallet_template, - Balances: pallet_balances, - Timestamp: pallet_timestamp, - SchellingGameShared: schelling_game_shared, - SortitionSumGame: sortition_sum_game, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - type AccountData = pallet_balances::AccountData; // New code -} - -parameter_types! { - pub const MinimumPeriod: u64 = 5; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU64<1>; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type MaxHolds = (); - type HoldIdentifier = (); - type AccountStore = System; -} - -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Currency = Balances; // New code - type SchellingGameSharedSource = SchellingGameShared; - type Slash = (); - type Reward = (); -} - -impl schelling_game_shared::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Currency = Balances; // New code - type RandomnessSource = TestRandomness; - type Slash = (); - type Reward = (); - type SortitionSumGameSource = SortitionSumGame; -} - -impl sortition_sum_game::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 100000), - (2, 200000), - (3, 300000), - (4, 300000), - (5, 300000), - (6, 300000), - (7, 300000), - (8, 300000), - (9, 300000), - (10, 300000), - (11, 300000), - (12, 300000), - (13, 300000), - (14, 300000), - (15, 300000), - (16, 300000), - (17, 300000), - (18, 300000), - (19, 300000), - (20, 300000), - (21, 300000), - (22, 300000), - (23, 300000), - (24, 300000), - (25, 300000), - (26, 300000), - (27, 300000), - (28, 300000), - (29, 300000), - (30, 300000), - (31, 300000), - (32, 300000), - (33, 300000), - (34, 300000), - (35, 300000), - ], - } // new code - .assimilate_storage(&mut t) - .unwrap(); - t.into() -} diff --git a/pallets/profile-validation/src/permissions.rs b/pallets/profile-validation/src/permissions.rs deleted file mode 100644 index 7db2110..0000000 --- a/pallets/profile-validation/src/permissions.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::*; - -impl Pallet { - pub(super) fn ensure_account_id_has_profile(account_id: T::AccountId) -> DispatchResult { - match >::get(&account_id) { - Some(_) => Ok(()), - None => Err(Error::::CitizenDoNotExists)?, - } - } -} diff --git a/pallets/profile-validation/src/tests.rs b/pallets/profile-validation/src/tests.rs deleted file mode 100644 index 7d41717..0000000 --- a/pallets/profile-validation/src/tests.rs +++ /dev/null @@ -1,468 +0,0 @@ -use crate::types::CitizenDetailsPost; -use crate::{mock::*, Error, Event}; -use frame_support::{assert_noop, assert_ok}; -use pallet_support::Content; -use pallet_support::WhoAndWhen; -use schelling_game_shared::types::Period; -use sortition_sum_game::types::SumTreeName; - -#[test] -fn add_citizen_profile_check() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - let data = ProfileValidation::citizen_profile(1); - let profile = Some(CitizenDetailsPost:: { - created: WhoAndWhen { account: 1, block: 1, time: 0 }, - content, - citizen_id: 1, - owner: 1, - edited: false, - hidden: false, - upvotes_count: 0, - downvotes_count: 0, - }); - assert_eq!(data, profile); - System::set_block_number(5); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqz" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - let data = ProfileValidation::citizen_profile(1); - let profile = Some(CitizenDetailsPost:: { - created: WhoAndWhen { account: 1, block: 5, time: 0 }, - content, - citizen_id: 1, - owner: 1, - edited: false, - hidden: false, - upvotes_count: 0, - downvotes_count: 0, - }); - assert_eq!(data, profile); - }); -} - -#[test] -fn check_fund_addition() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(10); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - let data = ProfileValidation::citizen_profile(1); - let profile = Some(CitizenDetailsPost:: { - created: WhoAndWhen { account: 1, block: 10, time: 0 }, - content, - citizen_id: 1, - owner: 1, - edited: false, - hidden: false, - upvotes_count: 0, - downvotes_count: 0, - }); - assert_eq!(data, profile); - let balance = Balances::free_balance(3); - assert_eq!(300000, balance); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(3), 1, 100)); - let balance = Balances::free_balance(3); - assert_eq!(300000 - 100, balance); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqz" - .as_bytes() - .to_vec(), - ); - assert_noop!( - ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone()), - Error::::NoMoreUpdates - ); - let data = ProfileValidation::profile_fund_details(1, 3).unwrap(); - assert_eq!(100, data.deposit); - let total_fund = ProfileValidation::total_fund_for_profile_collected(1); - assert_eq!(100, total_fund); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(3), 1, 100)); - let balance = Balances::free_balance(3); - assert_eq!(300000 - 200, balance); - let data = ProfileValidation::profile_fund_details(1, 3).unwrap(); - assert_eq!(200, data.deposit); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(4), 1, 500)); - let balance = Balances::free_balance(4); - assert_eq!(300000 - 500, balance); - let data = ProfileValidation::profile_fund_details(1, 4).unwrap(); - assert_eq!(500, data.deposit); - assert_noop!( - ProfileValidation::add_profile_stake(RuntimeOrigin::signed(5), 1, 1000), - Error::::AmountFundedGreaterThanRequired - ); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(5), 1, 300)); - System::assert_last_event(Event::ProfileFund { profile: 1, funder: 5 }.into()); - - let key = SumTreeName::ProfileValidation { citizen_address: 1, block_number: 10 }; - let period = SchellingGameShared::get_period(key); - assert_eq!(Some(Period::Evidence), period); - }) -} - -#[test] -fn challenge_evidence() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(3), 1, 1000)); - let key = SumTreeName::ProfileValidation { citizen_address: 1, block_number: 1 }; - let period = SchellingGameShared::get_period(key.clone()); - assert_eq!(Some(Period::Evidence), period); - - let challenge_content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" - .as_bytes() - .to_vec(), - ); - - let phase_data = ProfileValidation::get_phase_data(); - - assert_noop!( - ProfileValidation::challenge_profile( - RuntimeOrigin::signed(4), - 1, - challenge_content.clone() - ), - >::EvidencePeriodNotOver - ); - - System::set_block_number(phase_data.evidence_length + 1); - let fees = ProfileValidation::profile_registration_challenge_fees(); - let balance = Balances::free_balance(4); - assert_eq!(300000, balance); - assert_ok!(ProfileValidation::challenge_profile( - RuntimeOrigin::signed(4), - 1, - challenge_content.clone() - )); - let balance = Balances::free_balance(4); - assert_eq!(300000 - fees, balance); - let period = SchellingGameShared::get_period(key.clone()); - assert_eq!(Some(Period::Staking), period); - - assert_noop!( - ProfileValidation::challenge_profile( - RuntimeOrigin::signed(4), - 2, - challenge_content.clone() - ), - Error::::CitizenDoNotExists - ); - }) -} - -#[test] -fn challenge_profile_after_time_for_staking_over_test() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(3), 1, 1000)); - let key = SumTreeName::ProfileValidation { citizen_address: 1, block_number: 1 }; - let period = SchellingGameShared::get_period(key.clone()); - assert_eq!(Some(Period::Evidence), period); - - let challenge_content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" - .as_bytes() - .to_vec(), - ); - - let phase_data = ProfileValidation::get_phase_data(); - - assert_noop!( - ProfileValidation::challenge_profile( - RuntimeOrigin::signed(4), - 1, - challenge_content.clone() - ), - >::EvidencePeriodNotOver - ); - - System::set_block_number(phase_data.evidence_length + phase_data.end_of_staking_time + 1); - assert_noop!( - ProfileValidation::challenge_profile( - RuntimeOrigin::signed(4), - 1, - challenge_content.clone() - ), - >::TimeForStakingOver - ); - }); -} - -#[test] -fn return_profile_stake_test() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - let balance = Balances::free_balance(3); - assert_eq!(300000, balance); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(3), 1, 400)); - let balance = Balances::free_balance(3); - assert_eq!(300000 - 400, balance); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(4), 1, 600)); - let balance = Balances::free_balance(4); - assert_eq!(300000 - 600, balance); - let key = SumTreeName::ProfileValidation { citizen_address: 1, block_number: 1 }; - let period = SchellingGameShared::get_period(key.clone()); - assert_eq!(Some(Period::Evidence), period); - let phase_data = ProfileValidation::get_phase_data(); - System::set_block_number(phase_data.evidence_length + phase_data.end_of_staking_time); - assert_noop!( - ProfileValidation::return_profile_stake(RuntimeOrigin::signed(3), 1), - >::TimeForStakingNotOver - ); - System::set_block_number(phase_data.evidence_length + phase_data.end_of_staking_time + 1); - assert_ok!(ProfileValidation::return_profile_stake(RuntimeOrigin::signed(3), 1)); - let balance = Balances::free_balance(3); - assert_eq!(300000, balance); - assert_noop!( - ProfileValidation::return_profile_stake(RuntimeOrigin::signed(3), 1), - Error::::ProfileFundAlreadyReturned - ); - - assert_ok!(ProfileValidation::return_profile_stake(RuntimeOrigin::signed(4), 1)); - let balance = Balances::free_balance(4); - assert_eq!(300000, balance); - assert_noop!( - ProfileValidation::return_profile_stake(RuntimeOrigin::signed(5), 1), - Error::::ProfileFundNotExists - ); - }); -} - -#[test] -fn schelling_game_test() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(3), 1, 1000)); - let challenge_content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" - .as_bytes() - .to_vec(), - ); - let phase_data = ProfileValidation::get_phase_data(); - System::set_block_number(phase_data.evidence_length + 1); - assert_ok!(ProfileValidation::challenge_profile( - RuntimeOrigin::signed(4), - 1, - challenge_content.clone() - )); - - let balance = Balances::free_balance(29); - assert_eq!(300000, balance); - for j in 4..30 { - assert_ok!(ProfileValidation::apply_jurors(RuntimeOrigin::signed(j), 1, j * 100)); - } - - let balance = Balances::free_balance(29); - assert_eq!(300000 - 29 * 100, balance); - - assert_noop!( - ProfileValidation::draw_jurors(RuntimeOrigin::signed(5), 1, 5), - >::PeriodDontMatch - ); - - assert_noop!( - ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1), - >::StakingPeriodNotOver - ); - - System::set_block_number(phase_data.evidence_length + 1 + phase_data.staking_length); - - assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); - - assert_ok!(ProfileValidation::draw_jurors(RuntimeOrigin::signed(5), 1, 5)); - - let key = SumTreeName::ProfileValidation { citizen_address: 1, block_number: 1 }; - - let draws_in_round = SchellingGameShared::draws_in_round(key.clone()); - assert_eq!(5, draws_in_round); - - let drawn_jurors = SchellingGameShared::drawn_jurors(key.clone()); - assert_eq!(vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], drawn_jurors); - - assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); - - let period = SchellingGameShared::get_period(key.clone()); - - assert_eq!(Some(Period::Commit), period); - - let balance: u64 = Balances::free_balance(5); - assert_eq!(300000 - 5 * 100, balance); - assert_ok!(ProfileValidation::unstaking(RuntimeOrigin::signed(5), 1)); - let balance = Balances::free_balance(5); - assert_eq!(300000, balance); - - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_noop!( - ProfileValidation::commit_vote(RuntimeOrigin::signed(6), 1, hash), - >::JurorDoesNotExists - ); - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_ok!(ProfileValidation::commit_vote(RuntimeOrigin::signed(4), 1, hash)); - - // You can replace vote within the commit period. - let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); - assert_ok!(ProfileValidation::commit_vote(RuntimeOrigin::signed(4), 1, hash)); - - let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); - assert_ok!(ProfileValidation::commit_vote(RuntimeOrigin::signed(7), 1, hash)); - - let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); - assert_ok!(ProfileValidation::commit_vote(RuntimeOrigin::signed(13), 1, hash)); - - let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); - assert_ok!(ProfileValidation::commit_vote(RuntimeOrigin::signed(14), 1, hash)); - - let hash = sp_io::hashing::keccak_256("0salt6".as_bytes()); - assert_ok!(ProfileValidation::commit_vote(RuntimeOrigin::signed(15), 1, hash)); - - assert_noop!( - ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1), - >::CommitPeriodNotOver - ); - System::set_block_number( - phase_data.evidence_length + 1 + phase_data.staking_length + phase_data.commit_length, - ); - assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); - - assert_noop!( - ProfileValidation::reveal_vote( - RuntimeOrigin::signed(4), - 1, - 2, - "salt2".as_bytes().to_vec() - ), - >::CommitDoesNotMatch - ); - - assert_ok!(ProfileValidation::reveal_vote( - RuntimeOrigin::signed(4), - 1, - 1, - "salt2".as_bytes().to_vec() - )); - - assert_ok!(ProfileValidation::reveal_vote( - RuntimeOrigin::signed(7), - 1, - 1, - "salt3".as_bytes().to_vec() - )); - - assert_ok!(ProfileValidation::reveal_vote( - RuntimeOrigin::signed(13), - 1, - 1, - "salt4".as_bytes().to_vec() - )); - - assert_ok!(ProfileValidation::reveal_vote( - RuntimeOrigin::signed(14), - 1, - 1, - "salt5".as_bytes().to_vec() - )); - - assert_noop!( - ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1), - >::VotePeriodNotOver - ); - System::set_block_number( - phase_data.evidence_length - + 1 + phase_data.staking_length - + phase_data.commit_length - + phase_data.vote_length, - ); - assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); - - assert_noop!( - ProfileValidation::get_incentives(RuntimeOrigin::signed(15), 1), - >::VoteNotRevealed - ); - let balance: u64 = Balances::free_balance(14); - assert_eq!(300000 - 14 * 100, balance); - assert_ok!(ProfileValidation::get_incentives(RuntimeOrigin::signed(14), 1)); - let balance: u64 = Balances::free_balance(14); - assert_eq!(300025, balance); - }) -} - -#[test] -fn test_draw_juror() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProfileValidation::add_citizen(RuntimeOrigin::signed(1), content.clone())); - - assert_ok!(ProfileValidation::add_profile_stake(RuntimeOrigin::signed(3), 1, 1000)); - let challenge_content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhabc" - .as_bytes() - .to_vec(), - ); - let phase_data = ProfileValidation::get_phase_data(); - System::set_block_number(phase_data.evidence_length + 1); - assert_ok!(ProfileValidation::challenge_profile( - RuntimeOrigin::signed(4), - 1, - challenge_content.clone() - )); - - assert_ok!(ProfileValidation::apply_jurors(RuntimeOrigin::signed(5), 1, 100)); - assert_ok!(ProfileValidation::apply_jurors(RuntimeOrigin::signed(6), 1, 500)); - assert_ok!(ProfileValidation::apply_jurors(RuntimeOrigin::signed(7), 1, 1000)); - assert_ok!(ProfileValidation::apply_jurors(RuntimeOrigin::signed(8), 1, 1500)); - assert_ok!(ProfileValidation::apply_jurors(RuntimeOrigin::signed(9), 1, 2000)); - - System::set_block_number(phase_data.evidence_length + 1 + phase_data.staking_length); - - assert_ok!(ProfileValidation::pass_period(RuntimeOrigin::signed(5), 1)); - - assert_ok!(ProfileValidation::draw_jurors(RuntimeOrigin::signed(5), 1, 6)); - - // assert_ok!(ProfileValidation::draw_jurors(RuntimeOrigin::signed(5), 1, 5)); - }) -} diff --git a/pallets/profile-validation/src/types.rs b/pallets/profile-validation/src/types.rs deleted file mode 100644 index 0bcf118..0000000 --- a/pallets/profile-validation/src/types.rs +++ /dev/null @@ -1,54 +0,0 @@ -use frame_support::pallet_prelude::*; -use scale_info::TypeInfo; -// use frame_support::sp_std::{vec::Vec}; - -use super::*; - -pub const FIRST_CITIZEN_ID: CitizenId = 1; -pub const FIRST_CHALLENGE_POST_ID: ChallengePostId = 1; - -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CitizenDetailsPost { - pub created: WhoAndWhenOf, - pub content: Content, - pub citizen_id: CitizenId, - pub owner: T::AccountId, - pub edited: bool, - pub hidden: bool, - pub upvotes_count: u32, - pub downvotes_count: u32, -} - -#[derive( - PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, MaxEncodedLen, TypeInfo, -)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct ProfileFundInfo { - pub funder_account_id: AccountId, - pub validation_account_id: AccountId, - pub deposit: Balance, - pub deposit_returned: bool, -} - -#[derive( - PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, MaxEncodedLen, TypeInfo, -)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct ChallengerFundInfo { - pub challengerid: AccountId, - pub deposit: Balance, - pub start: BlockNumber, - pub challenge_completed: bool, -} - -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct ChallengeEvidencePost { - pub created: WhoAndWhenOf, - pub owner: T::AccountId, - pub kyc_profile_id: T::AccountId, - pub content: Content, - pub post_id_if_comment: Option, - pub is_comment: bool, -} diff --git a/pallets/project-tips/Cargo.toml b/pallets/project-tips/Cargo.toml deleted file mode 100644 index a4c1beb..0000000 --- a/pallets/project-tips/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -[package] -name = "project-tips" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-timestamp = { git = 'https://github.com/paritytech/substrate', branch = "polkadot-v0.9.42", default-features = false } -pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-support = { default-features = false, path = '../support' } -shared-storage = { default-features = false, path="../shared-storage"} -shared-storage-link = { default-features = false, path="../../traits/shared-storage-link"} -schelling-game-shared = {default-features = false, path = "../schelling-game-shared"} -schelling-game-shared-link = {default-features = false, path = "../../traits/schelling-game-shared-link"} -sortition-sum-game = {default-features = false, path="../sortition-sum-game"} - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support-test = { version = "3.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "pallet-timestamp/std", - "pallet-balances/std", - "pallet-support/std", - "shared-storage/std", - "schelling-game-shared/std", - "sortition-sum-game/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/project-tips/project-tips-rpc/Cargo.toml b/pallets/project-tips/project-tips-rpc/Cargo.toml deleted file mode 100644 index 7927d2f..0000000 --- a/pallets/project-tips/project-tips-rpc/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "project-tips-rpc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } -sc-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-api = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sc-rpc-api = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -project-tips-runtime-api = { default-features= false, path="../project-tips-runtime-api"} \ No newline at end of file diff --git a/pallets/project-tips/project-tips-runtime-api/Cargo.toml b/pallets/project-tips/project-tips-runtime-api/Cargo.toml deleted file mode 100644 index 5cee691..0000000 --- a/pallets/project-tips/project-tips-runtime-api/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "project-tips-runtime-api" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -sp-api = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42"} - -[features] -default = ["std"] -std = [ - "sp-api/std", - "frame-support/std", -] \ No newline at end of file diff --git a/pallets/project-tips/project-tips-runtime-api/src/lib.rs b/pallets/project-tips/project-tips-runtime-api/src/lib.rs deleted file mode 100644 index d98bf8f..0000000 --- a/pallets/project-tips/project-tips-runtime-api/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -// use frame_support::sp_std::{vec::Vec}; -// or -use frame_support::sp_std::prelude::*; -use sp_api::codec::Codec; - -type ProjectId = u64; - -sp_api::decl_runtime_apis! { - pub trait ProjectTipsApi where AccountId: Codec{ - - fn get_evidence_period_end_block(project_id: ProjectId) -> Option; - fn get_staking_period_end_block(project_id: ProjectId) -> Option; - fn get_drawing_period_end(project_id: ProjectId) -> (u64, u64, bool); - fn get_commit_period_end_block(project_id: ProjectId) -> Option; - fn get_vote_period_end_block(project_id: ProjectId) -> Option; - fn selected_as_juror(project_id: ProjectId, who: AccountId) -> bool; - } -} diff --git a/pallets/project-tips/src/benchmarking.rs b/pallets/project-tips/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/project-tips/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/project-tips/src/extras.rs b/pallets/project-tips/src/extras.rs deleted file mode 100644 index bdd0fc9..0000000 --- a/pallets/project-tips/src/extras.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::*; - -impl Project { - pub fn new( - project_id: ProjectId, - department_id: DepartmentId, - content: Content, - tipping_name: TippingName, - funding_needed: BalanceOf, - project_leader: T::AccountId, - ) -> Self { - Project { - created: new_who_and_when::(project_leader.clone()), - project_id, - department_id, - content, - tipping_name, - funding_needed, - project_leader, - } - } -} - -impl Pallet { - pub(super) fn get_phase_data() -> PhaseData { - T::SchellingGameSharedSource::create_phase_data(50, 5, 3, 100, (100, 100)) - } - - pub fn ensure_user_is_project_creator_and_project_exists( - project_id: ProjectId, - user: T::AccountId, - ) -> DispatchResult { - let project_option: Option> = >::get(project_id); - match project_option { - Some(project) => { - let project_leader = project.project_leader; - ensure!(project_leader == user, Error::::ProjectCreatorDontMatch); - }, - None => Err(Error::::ProjectDontExists)?, - } - - Ok(()) - } - - pub fn ensure_staking_period_set_once_project_id(project_id: ProjectId) -> DispatchResult { - let block_number_option = >::get(project_id); - match block_number_option { - Some(_block) => Err(Error::::ProjectIdStakingPeriodAlreadySet)?, - None => Ok(()), - } - } - - pub fn get_block_number_of_schelling_game( - project_id: ProjectId, - ) -> Result, DispatchError> { - let block_number_option = >::get(project_id); - let block_number = match block_number_option { - Some(block_number) => block_number, - None => Err(Error::::BlockNumberProjectIdNotExists)?, - }; - Ok(block_number) - } - - pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { - input.saturated_into::>() - } - - pub(super) fn u64_to_block_saturated(input: u64) -> BlockNumberOf { - input.saturated_into::>() - } - - pub fn value_of_tipping_name(tipping: TippingName) -> TippingValue> { - match tipping { - TippingName::SmallTipper => TippingValue { - max_tipping_value: 10_000u64.saturated_into::>(), - stake_required: 10u64.saturated_into::>(), - }, - TippingName::BigTipper => TippingValue { - max_tipping_value: 100_000u64.saturated_into::>(), - stake_required: 50u64.saturated_into::>(), - }, - TippingName::SmallSpender => TippingValue { - max_tipping_value: 1_000_000u64.saturated_into::>(), - stake_required: 100u64.saturated_into::>(), - }, - TippingName::MediumSpender => TippingValue { - max_tipping_value: 10_000_000u64.saturated_into::>(), - stake_required: 200u64.saturated_into::>(), - }, - TippingName::BigSpender => TippingValue { - max_tipping_value: 100_000_000u64.saturated_into::>(), - stake_required: 500u64.saturated_into::>(), - }, - } - } - - // Block code start - - pub fn get_evidence_period_end_block(project_id: ProjectId) -> Option { - let now = >::block_number(); - - let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_evidence_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_staking_period_end_block(project_id: ProjectId) -> Option { - let now = >::block_number(); - - let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_staking_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_drawing_period_end(project_id: ProjectId) -> (u64, u64, bool) { - let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - let phase_data = Self::get_phase_data(); - - let result = - T::SchellingGameSharedSource::get_drawing_period_end_helper_link(key, phase_data); - result - } - - pub fn get_commit_period_end_block(project_id: ProjectId) -> Option { - let now = >::block_number(); - - let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_commit_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn get_vote_period_end_block(project_id: ProjectId) -> Option { - let now = >::block_number(); - - let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let phase_data = Self::get_phase_data(); - - let result = T::SchellingGameSharedSource::get_vote_period_end_block_helper_link( - key, phase_data, now, - ); - result - } - - pub fn selected_as_juror(project_id: ProjectId, who: T::AccountId) -> bool { - let block_number = Self::get_block_number_of_schelling_game(project_id).unwrap(); - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let result = T::SchellingGameSharedSource::selected_as_juror_helper_link(key, who); - result - } - - // Block code end -} diff --git a/pallets/project-tips/src/lib.rs b/pallets/project-tips/src/lib.rs deleted file mode 100644 index ad3bd08..0000000 --- a/pallets/project-tips/src/lib.rs +++ /dev/null @@ -1,369 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -// One can enhance validation measures by increasing staking power for local residents or individuals with positive externalities—those who contribute to the network for a good cause. -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -mod extras; -mod types; - -use frame_support::sp_runtime::traits::Saturating; -use frame_support::sp_runtime::SaturatedConversion; -use frame_support::sp_std::prelude::*; -use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - ensure, -}; -use frame_support::{ - traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, - PalletId, -}; -use pallet_support::{ - ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, - WhoAndWhen, WhoAndWhenOf, -}; -use schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType}; -use schelling_game_shared_link::SchellingGameSharedLink; -use shared_storage_link::SharedStorageLink; -use sortition_sum_game::types::SumTreeName; -pub use types::PROJECT_ID; -use types::{Project, TippingName, TippingValue}; - -type AccountIdOf = ::AccountId; -type BalanceOf = <::Currency as Currency>>::Balance; -pub type BlockNumberOf = ::BlockNumber; -pub type SumTreeNameType = SumTreeName, BlockNumberOf>; -type DepartmentId = u64; -type ProjectId = u64; - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: - frame_system::Config + schelling_game_shared::Config + pallet_timestamp::Config - { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - - type SharedStorageSource: SharedStorageLink>; - type SchellingGameSharedSource: SchellingGameSharedLink< - SumTreeName = SumTreeName, - SchellingGameType = SchellingGameType, - BlockNumber = Self::BlockNumber, - AccountId = AccountIdOf, - Balance = BalanceOf, - RangePoint = RangePoint, - Period = Period, - PhaseData = PhaseData, - >; - type Currency: ReservableCurrency; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::type_value] - pub fn MinimumDepartmentStake() -> BalanceOf { - 10000u128.saturated_into::>() - } - - #[pallet::type_value] - pub fn DefaultForNextProjectId() -> ProjectId { - PROJECT_ID - } - - #[pallet::storage] - #[pallet::getter(fn next_project_id)] - pub type NextProjectId = - StorageValue<_, ProjectId, ValueQuery, DefaultForNextProjectId>; - - #[pallet::storage] - #[pallet::getter(fn get_project)] - pub type Projects = StorageMap<_, Blake2_128Concat, ProjectId, Project>; - - // #[pallet::storage] - // #[pallet::getter(fn department_stake)] - // pub type DepartmentStakeBalance = - // StorageMap<_, Twox64Concat, DepartmentId, BalanceOf, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn validation_block)] - pub type ValidationBlock = - StorageMap<_, Blake2_128Concat, ProjectId, BlockNumberOf>; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { - something: u32, - who: T::AccountId, - }, - ProjectCreated { - account: T::AccountId, - project_id: ProjectId, - }, - StakinPeriodStarted { - project_id: ProjectId, - block_number: BlockNumberOf, - }, - ApplyJurors { - project_id: ProjectId, - block_number: BlockNumberOf, - account: T::AccountId, - }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - LessThanMinStake, - CannotStakeNow, - ChoiceOutOfRange, - FundingMoreThanTippingValue, - ProjectDontExists, - ProjectCreatorDontMatch, - ProjectIdStakingPeriodAlreadySet, - BlockNumberProjectIdNotExists, - } - - // Check deparment exists, it will done using loose coupling - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(0)] - pub fn create_project( - origin: OriginFor, - department_id: DepartmentId, - content: Content, - tipping_name: TippingName, - funding_needed: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let new_project_id = Self::next_project_id(); - let tipping_value = Self::value_of_tipping_name(tipping_name); - let max_tipping_value = tipping_value.max_tipping_value; - ensure!( - funding_needed <= max_tipping_value, - Error::::FundingMoreThanTippingValue - ); - let new_project: Project = Project::new( - new_project_id, - department_id, - content, - tipping_name, - funding_needed, - who.clone(), - ); - - Projects::insert(new_project_id, new_project); - NextProjectId::::mutate(|n| { - *n += 1; - }); - - Self::deposit_event(Event::ProjectCreated { account: who, project_id: new_project_id }); - - Ok(()) - } - - - - // Check update and discussion time over, only project creator can apply staking period - #[pallet::call_index(1)] - #[pallet::weight(0)] - pub fn apply_staking_period(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - let who = ensure_signed(origin)?; - - Self::ensure_user_is_project_creator_and_project_exists(project_id, who.clone())?; - Self::ensure_staking_period_set_once_project_id(project_id)?; - match >::get(project_id) { - Some(project) => { - let tipping_name = project.tipping_name; - let tipping_value = Self::value_of_tipping_name(tipping_name); - let stake_required = tipping_value.stake_required; - - let _ = ::Currency::withdraw( - &who, - stake_required, - WithdrawReasons::TRANSFER, - ExistenceRequirement::AllowDeath, - )?; - }, - - None => Err(Error::::ProjectDontExists)?, - } - - let now = >::block_number(); - - let key = SumTreeName::ProjectTips { project_id, block_number: now.clone() }; - - >::insert(project_id, now.clone()); - // check what if called again, its done with `ensure_staking_period_set_once_project_id` - T::SchellingGameSharedSource::set_to_staking_period_pe_link(key.clone(), now.clone())?; - T::SchellingGameSharedSource::create_tree_helper_link(key, 3)?; - - Self::deposit_event(Event::StakinPeriodStarted { project_id, block_number: now }); - - Ok(()) - } - - #[pallet::call_index(2)] - #[pallet::weight(0)] - pub fn apply_jurors( - origin: OriginFor, - project_id: ProjectId, - stake: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let block_number = Self::get_block_number_of_schelling_game(project_id)?; - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::apply_jurors_helper_link( - key, - phase_data, - who.clone(), - stake, - )?; - Self::deposit_event(Event::ApplyJurors { project_id, block_number, account: who }); - - Ok(()) - } - - #[pallet::call_index(3)] - #[pallet::weight(0)] - pub fn pass_period(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - let _who = ensure_signed(origin)?; - - let block_number = Self::get_block_number_of_schelling_game(project_id)?; - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let now = >::block_number(); - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::change_period_link(key, phase_data, now)?; - Ok(()) - } - - #[pallet::call_index(4)] - #[pallet::weight(0)] - pub fn draw_jurors( - origin: OriginFor, - project_id: ProjectId, - iterations: u64, - ) -> DispatchResult { - let _who = ensure_signed(origin)?; - - let block_number = Self::get_block_number_of_schelling_game(project_id)?; - - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let phase_data = Self::get_phase_data(); - - T::SchellingGameSharedSource::draw_jurors_helper_link(key, phase_data, iterations)?; - - Ok(()) - } - - // Unstaking - // Stop drawn juror to unstake ✔️ - #[pallet::call_index(5)] - #[pallet::weight(0)] - pub fn unstaking(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = Self::get_block_number_of_schelling_game(project_id)?; - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - T::SchellingGameSharedSource::unstaking_helper_link(key, who)?; - Ok(()) - } - - #[pallet::call_index(6)] - #[pallet::weight(0)] - pub fn commit_vote( - origin: OriginFor, - project_id: ProjectId, - vote_commit: [u8; 32], - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = Self::get_block_number_of_schelling_game(project_id)?; - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - T::SchellingGameSharedSource::commit_vote_helper_link(key, who, vote_commit)?; - Ok(()) - } - - #[pallet::call_index(7)] - #[pallet::weight(0)] - pub fn reveal_vote( - origin: OriginFor, - project_id: ProjectId, - choice: u128, - salt: Vec, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let block_number = Self::get_block_number_of_schelling_game(project_id)?; - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - T::SchellingGameSharedSource::reveal_vote_two_choice_helper_link( - key, who, choice, salt, - )?; - Ok(()) - } - - #[pallet::call_index(8)] - #[pallet::weight(0)] - pub fn get_incentives(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - let who = ensure_signed(origin)?; - let block_number = Self::get_block_number_of_schelling_game(project_id)?; - let key = SumTreeName::ProjectTips { project_id, block_number: block_number.clone() }; - - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( - key, phase_data, who, - )?; - Ok(()) - } - } -} diff --git a/pallets/project-tips/src/mock.rs b/pallets/project-tips/src/mock.rs deleted file mode 100644 index 91a2be6..0000000 --- a/pallets/project-tips/src/mock.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate as pallet_template; -use frame_support::{ - parameter_types, - traits::{ConstU16, ConstU64, GenesisBuild}, -}; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; -use frame_support_test::TestRandomness; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - ProjectTips: pallet_template, - Balances: pallet_balances, - Timestamp: pallet_timestamp, - SharedStorage:shared_storage, - SchellingGameShared: schelling_game_shared, - SortitionSumGame: sortition_sum_game, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - type AccountData = pallet_balances::AccountData; // New code -} - -parameter_types! { - pub const MinimumPeriod: u64 = 5; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -impl shared_storage::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type SharedStorageSource = SharedStorage; - type Currency = Balances; // New code - type SchellingGameSharedSource = SchellingGameShared; -} - -impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU64<1>; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type MaxHolds = (); - type HoldIdentifier = (); - type AccountStore = System; -} - -impl schelling_game_shared::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Currency = Balances; // New code - type RandomnessSource = TestRandomness; - type Slash = (); - type Reward = (); - type SortitionSumGameSource = SortitionSumGame; -} - -impl sortition_sum_game::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 100000), - (2, 200000), - (3, 300000), - (4, 300000), - (5, 300000), - (6, 300000), - (7, 300000), - (8, 300000), - (9, 300000), - (10, 300000), - (11, 300000), - (12, 300000), - (13, 300000), - (14, 300000), - (15, 300000), - (16, 300000), - (17, 300000), - (18, 300000), - (19, 300000), - (20, 300000), - (21, 300000), - (22, 300000), - (23, 300000), - (24, 300000), - (25, 300000), - (26, 300000), - (27, 300000), - (28, 300000), - (29, 300000), - (30, 300000), - (31, 300000), - (32, 300000), - (33, 300000), - (34, 300000), - (35, 300000), - ], - } // new code - .assimilate_storage(&mut t) - .unwrap(); - shared_storage::GenesisConfig:: { approved_citizen_address: vec![1, 2] } - .assimilate_storage(&mut t) - .unwrap(); - t.into() -} diff --git a/pallets/project-tips/src/tests.rs b/pallets/project-tips/src/tests.rs deleted file mode 100644 index c29e03d..0000000 --- a/pallets/project-tips/src/tests.rs +++ /dev/null @@ -1,290 +0,0 @@ -use crate::types::TippingName; -use crate::{mock::*, Error, Event}; -use frame_support::{assert_noop, assert_ok}; -use pallet_support::Content; -use schelling_game_shared::types::Period; -use sortition_sum_game::types::SumTreeName; - -#[test] -fn check_balance_on_staking() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - let tipping_name = TippingName::SmallTipper; - let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); - let max_tipping_value = tipping_value.max_tipping_value; - let stake_required = tipping_value.stake_required; - let funding_needed = max_tipping_value - 100; - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProjectTips::create_project( - RuntimeOrigin::signed(1), - 2, - content.clone(), - tipping_name, - funding_needed - )); - - System::assert_last_event(Event::ProjectCreated { account: 1, project_id: 1 }.into()); - - let balance = Balances::free_balance(1); - - assert_ok!(ProjectTips::apply_staking_period(RuntimeOrigin::signed(1), 1)); - - let after_balance = Balances::free_balance(1); - - assert_eq!(after_balance, balance - stake_required); - }); -} - -#[test] -fn check_create_project_function() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let tipping_name = TippingName::SmallTipper; - let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); - let max_tipping_value = tipping_value.max_tipping_value; - let funding_needed = max_tipping_value - 100; - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProjectTips::create_project( - RuntimeOrigin::signed(1), - 2, - content.clone(), - tipping_name, - funding_needed - )); - - - - System::assert_last_event(Event::ProjectCreated { account: 1, project_id: 1 }.into()); - - let next_project_id = ProjectTips::next_project_id(); - - assert_eq!(2, next_project_id); - - let funding_needed = max_tipping_value + 100; - - assert_noop!( - ProjectTips::create_project(RuntimeOrigin::signed(1), 2, content, tipping_name, funding_needed), - Error::::FundingMoreThanTippingValue - ); - }); -} - -#[test] -fn check_apply_staking_period_function() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - assert_noop!( - ProjectTips::apply_staking_period(RuntimeOrigin::signed(1), 2), - Error::::ProjectDontExists - ); - - let tipping_name = TippingName::SmallTipper; - let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); - let max_tipping_value = tipping_value.max_tipping_value; - let funding_needed = max_tipping_value - 100; - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProjectTips::create_project( - RuntimeOrigin::signed(1), - 2, - content, - tipping_name, - funding_needed - )); - - assert_noop!( - ProjectTips::apply_staking_period(RuntimeOrigin::signed(3), 1), - Error::::ProjectCreatorDontMatch - ); - - assert_ok!(ProjectTips::apply_staking_period(RuntimeOrigin::signed(1), 1)); - - System::assert_last_event( - Event::StakinPeriodStarted { project_id: 1, block_number: 1 }.into(), - ); - System::set_block_number(5); - assert_noop!( - ProjectTips::apply_staking_period(RuntimeOrigin::signed(1), 1), - Error::::ProjectIdStakingPeriodAlreadySet - ); - }); -} - -#[test] -fn schelling_game_test() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let tipping_name = TippingName::SmallTipper; - let tipping_value = ProjectTips::value_of_tipping_name(tipping_name); - let max_tipping_value = tipping_value.max_tipping_value; - let stake_required = tipping_value.stake_required; - let funding_needed = max_tipping_value - 100; - let content: Content = Content::IPFS( - "bafkreiaiq24be2iioasr6ftyaum3icmj7amtjkom2jeokov5k5ojwzhvqy" - .as_bytes() - .to_vec(), - ); - assert_ok!(ProjectTips::create_project( - RuntimeOrigin::signed(1), - 2, - content, - tipping_name, - funding_needed - )); - - let balance = Balances::free_balance(1); - - assert_ok!(ProjectTips::apply_staking_period(RuntimeOrigin::signed(1), 1)); - - let after_balance = Balances::free_balance(1); - - assert_eq!(after_balance, balance - stake_required); - - let phase_data = ProjectTips::get_phase_data(); - - let balance = Balances::free_balance(29); - assert_eq!(300000, balance); - for j in 4..30 { - assert_ok!(ProjectTips::apply_jurors(RuntimeOrigin::signed(j), 1, j * 100)); - } - - let balance = Balances::free_balance(29); - assert_eq!(300000 - 29 * 100, balance); - - assert_noop!( - ProjectTips::draw_jurors(RuntimeOrigin::signed(5), 1, 5), - >::PeriodDontMatch - ); - - assert_noop!( - ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), - >::StakingPeriodNotOver - ); - - System::set_block_number(1 + phase_data.staking_length); - - assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); - - assert_ok!(ProjectTips::draw_jurors(RuntimeOrigin::signed(5), 1, 5)); - - let key = SumTreeName::ProjectTips { project_id: 1, block_number: 1 }; - - let draws_in_round = SchellingGameShared::draws_in_round(key.clone()); - assert_eq!(5, draws_in_round); - - let drawn_jurors = SchellingGameShared::drawn_jurors(key.clone()); - assert_eq!(vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], drawn_jurors); - - assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); - - let period = SchellingGameShared::get_period(key.clone()); - - assert_eq!(Some(Period::Commit), period); - - let balance: u64 = Balances::free_balance(5); - assert_eq!(300000 - 5 * 100, balance); - assert_ok!(ProjectTips::unstaking(RuntimeOrigin::signed(5), 1)); - let balance = Balances::free_balance(5); - assert_eq!(300000, balance); - - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_noop!( - ProjectTips::commit_vote(RuntimeOrigin::signed(6), 1, hash), - >::JurorDoesNotExists - ); - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(4), 1, hash)); - - // You can replace vote within the commit period. - let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); - assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(4), 1, hash)); - - let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); - assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(7), 1, hash)); - - let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); - assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(13), 1, hash)); - - let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); - assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(14), 1, hash)); - - let hash = sp_io::hashing::keccak_256("0salt6".as_bytes()); - assert_ok!(ProjectTips::commit_vote(RuntimeOrigin::signed(15), 1, hash)); - - assert_noop!( - ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), - >::CommitPeriodNotOver - ); - System::set_block_number( - phase_data.evidence_length + 1 + phase_data.staking_length + phase_data.commit_length, - ); - assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); - - assert_noop!( - ProjectTips::reveal_vote(RuntimeOrigin::signed(4), 1, 2, "salt2".as_bytes().to_vec()), - >::CommitDoesNotMatch - ); - - assert_ok!(ProjectTips::reveal_vote( - RuntimeOrigin::signed(4), - 1, - 1, - "salt2".as_bytes().to_vec() - )); - - assert_ok!(ProjectTips::reveal_vote( - RuntimeOrigin::signed(7), - 1, - 1, - "salt3".as_bytes().to_vec() - )); - - assert_ok!(ProjectTips::reveal_vote( - RuntimeOrigin::signed(13), - 1, - 1, - "salt4".as_bytes().to_vec() - )); - - assert_ok!(ProjectTips::reveal_vote( - RuntimeOrigin::signed(14), - 1, - 1, - "salt5".as_bytes().to_vec() - )); - - assert_noop!( - ProjectTips::pass_period(RuntimeOrigin::signed(5), 1), - >::VotePeriodNotOver - ); - System::set_block_number( - phase_data.evidence_length - + 1 + phase_data.staking_length - + phase_data.commit_length - + phase_data.vote_length, - ); - assert_ok!(ProjectTips::pass_period(RuntimeOrigin::signed(5), 1)); - - assert_noop!( - ProjectTips::get_incentives(RuntimeOrigin::signed(15), 1), - >::VoteNotRevealed - ); - let balance: u64 = Balances::free_balance(14); - assert_eq!(300000 - 14 * 100, balance); - assert_ok!(ProjectTips::get_incentives(RuntimeOrigin::signed(14), 1)); - let balance: u64 = Balances::free_balance(14); - assert_eq!(300025, balance); - }) -} diff --git a/pallets/project-tips/src/types.rs b/pallets/project-tips/src/types.rs deleted file mode 100644 index 6e15b5c..0000000 --- a/pallets/project-tips/src/types.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; -use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -use frame_support::pallet_prelude::*; -use scale_info::TypeInfo; - -pub const PROJECT_ID: ProjectId = 1; - -#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub enum TippingName { - SmallTipper, - BigTipper, - SmallSpender, - MediumSpender, - BigSpender, -} - -#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct TippingValue { - pub max_tipping_value: Balance, - pub stake_required: Balance, -} - -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct Project { - pub created: WhoAndWhenOf, - pub project_id: ProjectId, - pub department_id: DepartmentId, - pub content: Content, - pub tipping_name: TippingName, - pub funding_needed: BalanceOf, - pub project_leader: T::AccountId, -} diff --git a/pallets/registrar/Cargo.toml b/pallets/registrar/Cargo.toml new file mode 100644 index 0000000..8ddeb26 --- /dev/null +++ b/pallets/registrar/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "pallet-registrar" +authors = { workspace = true } +description = "ParaRegistrar pallet that allows to register and deregister ParaIds" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +pallet-configuration = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +tp-container-chain-genesis-data = { workspace = true } +tp-traits = { workspace = true } + +[dev-dependencies] +pallet-balances = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances/std", + "pallet-configuration/std", + "parity-scale-codec/std", + "scale-info/std", + "serde/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-container-chain-genesis-data/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-configuration/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-configuration/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/registrar/rpc/runtime-api/Cargo.toml b/pallets/registrar/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000..31dd379 --- /dev/null +++ b/pallets/registrar/rpc/runtime-api/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "pallet-registrar-runtime-api" +authors = { workspace = true } +description = "Runtime API definition of pallet-registrar" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +frame-support = { workspace = true } +pallet-registrar = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-api = { workspace = true } +tp-container-chain-genesis-data = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "frame-support/std", + "pallet-registrar/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-api/std", + "tp-container-chain-genesis-data/std", +] diff --git a/pallets/registrar/rpc/runtime-api/src/lib.rs b/pallets/registrar/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000..5733f92 --- /dev/null +++ b/pallets/registrar/rpc/runtime-api/src/lib.rs @@ -0,0 +1,54 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Runtime API for Registrar pallet + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use tp_container_chain_genesis_data::ContainerChainGenesisData; +use {frame_support::traits::Get, scale_info::prelude::vec::Vec}; + +sp_api::decl_runtime_apis! { + pub trait RegistrarApi where + ParaId: parity_scale_codec::Codec, + MaxLengthTokenSymbol: Get, + { + /// Return the registered para ids + fn registered_paras() -> Vec; + + /// Fetch genesis data for this para id + fn genesis_data(para_id: ParaId) -> Option>; + + /// Fetch boot_nodes for this para id + fn boot_nodes(para_id: ParaId) -> Vec>; + } +} + +sp_api::decl_runtime_apis! { + pub trait OnDemandBlockProductionApi where + ParaId: parity_scale_codec::Codec, + Slot: parity_scale_codec::Codec, + { + /// Return the minimum number of slots that must pass between to blocks before parathread collators can propose + /// the next block. + /// + /// # Returns + /// + /// * `Some(min)`, where the condition for the slot to be valid is `(slot - parent_slot) >= min`. + /// * `None` if the `para_id` is not a parathread. + fn min_slot_freq(para_id: ParaId) -> Option; + } +} diff --git a/pallets/registrar/src/benchmarks.rs b/pallets/registrar/src/benchmarks.rs new file mode 100644 index 0000000..8298d41 --- /dev/null +++ b/pallets/registrar/src/benchmarks.rs @@ -0,0 +1,452 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#![cfg(feature = "runtime-benchmarks")] + +//! Benchmarking +use { + crate::{Call, Config, DepositBalanceOf, Pallet, RegistrarHooks}, + frame_benchmarking::{account, v2::*}, + frame_support::traits::Currency, + frame_system::RawOrigin, + sp_core::Get, + sp_std::{vec, vec::Vec}, + tp_container_chain_genesis_data::{ContainerChainGenesisData, ContainerChainGenesisDataItem}, + tp_traits::{ParaId, SlotFrequency}, +}; + +/// Create a funded user. +/// Used for generating the necessary amount for registering +fn create_funded_user( + string: &'static str, + n: u32, + extra: DepositBalanceOf, +) -> (T::AccountId, DepositBalanceOf) { + const SEED: u32 = 0; + let user = account(string, n, SEED); + let min_reserve_amount = T::DepositAmount::get(); + let total = min_reserve_amount + extra; + T::Currency::make_free_balance_be(&user, total); + let _ = T::Currency::issue(total); + (user, total) +} + +#[benchmarks] +mod benchmarks { + use super::*; + + fn new_genesis_data>( + storage: Vec, + ) -> ContainerChainGenesisData { + ContainerChainGenesisData { + storage, + name: Default::default(), + id: Default::default(), + fork_id: Default::default(), + extensions: Default::default(), + properties: Default::default(), + } + } + + // Returns number of para ids in pending verification (registered but not marked as valid) + fn pending_verification_len() -> usize { + crate::PendingVerification::::iter_keys().count() + } + + #[benchmark] + fn register(x: Linear<5, 3_000_000>, y: Linear<1, 50>, z: Linear<1, 10>) { + let mut data = vec![]; + // Number of keys + for _i in 1..z { + data.push((b"code".to_vec(), vec![1; (x / z) as usize]).into()) + } + + let storage = new_genesis_data(data); + + for i in 1..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + storage.clone(), + ) + .unwrap(); + } + + // We should have registered y-1 + assert_eq!(pending_verification_len::(), (y - 1) as usize); + + let (caller, _deposit_amount) = + create_funded_user::("caller", 0, T::DepositAmount::get()); + + #[extrinsic_call] + Pallet::::register(RawOrigin::Signed(caller), Default::default(), storage); + + // verification code + assert_eq!(pending_verification_len::(), y as usize); + assert!(Pallet::::registrar_deposit(ParaId::default()).is_some()); + } + + #[benchmark] + fn deregister_immediate(x: Linear<5, 3_000_000>, y: Linear<1, 50>) { + let storage = vec![(b"code".to_vec(), vec![1; x as usize]).into()]; + let storage = new_genesis_data(storage); + + for i in 0..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + storage.clone(), + ) + .unwrap(); + // Do not call mark_valid_for_collating, to ensure that the deregister call also executes the cleanup hooks + } + + // We should have registered y + assert_eq!(pending_verification_len::(), y as usize); + assert!(Pallet::::registrar_deposit(ParaId::from(y - 1)).is_some()); + + #[extrinsic_call] + Pallet::::deregister(RawOrigin::Root, (y - 1).into()); + + // We should have y-1 + assert_eq!(pending_verification_len::(), (y - 1) as usize); + assert!(Pallet::::registrar_deposit(ParaId::from(y - 1)).is_none()); + } + + #[benchmark] + fn deregister_scheduled(x: Linear<5, 3_000_000>, y: Linear<1, 50>) { + let storage = vec![(b"code".to_vec(), vec![1; x as usize]).into()]; + let storage = new_genesis_data(storage); + let genesis_para_id_len = Pallet::::registered_para_ids().len(); + + for i in 0..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + storage.clone(), + ) + .unwrap(); + // Call mark_valid_for_collating to ensure that the deregister call + // does not execute the cleanup hooks immediately + T::RegistrarHooks::benchmarks_ensure_valid_for_collating(i.into()); + Pallet::::mark_valid_for_collating(RawOrigin::Root.into(), i.into()).unwrap(); + } + + // Start a new session + Pallet::::initializer_on_new_session(&T::SessionDelay::get()); + // We should have registered y + assert_eq!( + Pallet::::registered_para_ids().len(), + genesis_para_id_len + y as usize + ); + assert!(Pallet::::registrar_deposit(ParaId::from(y - 1)).is_some()); + + #[extrinsic_call] + Pallet::::deregister(RawOrigin::Root, (y - 1).into()); + + // We now have y - 1 but the deposit has not been removed yet + assert_eq!( + Pallet::::pending_registered_para_ids()[0].1.len(), + genesis_para_id_len + (y - 1) as usize + ); + assert!(Pallet::::registrar_deposit(ParaId::from(y - 1)).is_some()); + + // Start a new session + Pallet::::initializer_on_new_session(&T::SessionDelay::get()); + + // Now it has been removed + assert_eq!( + Pallet::::registered_para_ids().len(), + genesis_para_id_len + (y - 1) as usize + ); + assert!(Pallet::::registrar_deposit(ParaId::from(y - 1)).is_none()); + } + + #[benchmark] + fn mark_valid_for_collating(y: Linear<1, 50>) { + let storage = vec![(vec![1; 4], vec![1; 3_000_000usize]).into()]; + let storage = new_genesis_data(storage); + + // Worst case: when RegisteredParaIds and PendingVerification are both full + // First loop to fill PendingVerification to its maximum + for i in 0..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + storage.clone(), + ) + .unwrap(); + } + + // Second loop to fill RegisteredParaIds to its maximum + for k in 1000..(1000 + y) { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", k, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + k.into(), + storage.clone(), + ) + .unwrap(); + T::RegistrarHooks::benchmarks_ensure_valid_for_collating(k.into()); + Pallet::::mark_valid_for_collating(RawOrigin::Root.into(), k.into()).unwrap(); + } + + // Start a new session + Pallet::::initializer_on_new_session(&T::SessionDelay::get()); + + // We should have registered y + assert_eq!(pending_verification_len::(), y as usize); + T::RegistrarHooks::benchmarks_ensure_valid_for_collating((y - 1).into()); + + #[extrinsic_call] + Pallet::::mark_valid_for_collating(RawOrigin::Root, (y - 1).into()); + + // We should have y-1 + assert_eq!(pending_verification_len::(), (y - 1) as usize); + } + + #[benchmark] + fn pause_container_chain(y: Linear<1, 50>) { + let storage = vec![(vec![1; 4], vec![1; 3_000_000usize]).into()]; + let storage = new_genesis_data(storage); + + // Deregister all the existing chains to avoid conflicts with the new ones + for para_id in Pallet::::registered_para_ids() { + Pallet::::deregister(RawOrigin::Root.into(), para_id).unwrap(); + } + + // Worst case: when RegisteredParaIds and Paused are both full + // First loop to fill RegisteredParaIds to its maximum + for i in 0..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + storage.clone(), + ) + .unwrap(); + T::RegistrarHooks::benchmarks_ensure_valid_for_collating(i.into()); + Pallet::::mark_valid_for_collating(RawOrigin::Root.into(), i.into()).unwrap(); + } + + // Second loop to fill Paused to its maximum + for k in 1000..(1000 + y) { + let (caller, _deposit_amount) = + create_funded_user::("caller", k, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + k.into(), + storage.clone(), + ) + .unwrap(); + T::RegistrarHooks::benchmarks_ensure_valid_for_collating(k.into()); + Pallet::::mark_valid_for_collating(RawOrigin::Root.into(), k.into()).unwrap(); + Pallet::::pause_container_chain(RawOrigin::Root.into(), k.into()).unwrap(); + } + + // Check PendingPaused has a length of y + assert_eq!(Pallet::::pending_paused()[0].1.len(), y as usize); + // Check y-1 is not in PendingPaused + assert!(!Pallet::::pending_paused()[0] + .1 + .contains(&ParaId::from(y - 1))); + // Check y-1 is in pending_registered_para_ids + assert!(Pallet::::pending_registered_para_ids()[0] + .1 + .contains(&ParaId::from(y - 1))); + + #[extrinsic_call] + Pallet::::pause_container_chain(RawOrigin::Root, (y - 1).into()); + + // Start a new session + Pallet::::initializer_on_new_session(&T::SessionDelay::get()); + + // Check y-1 is in Paused + assert!(Pallet::::paused().contains(&ParaId::from(y - 1))); + // Check y-1 is not in registered_para_ids + assert!(!Pallet::::registered_para_ids().contains(&ParaId::from(y - 1))); + } + + #[benchmark] + fn unpause_container_chain(y: Linear<1, 50>) { + let storage = vec![(vec![1; 4], vec![1; 3_000_000usize]).into()]; + let storage = new_genesis_data(storage); + + // Deregister all the existing chains to avoid conflicts with the new ones + for para_id in Pallet::::registered_para_ids() { + Pallet::::deregister(RawOrigin::Root.into(), para_id).unwrap(); + } + + // Worst case: when RegisteredParaIds and Paused are both full + // First loop to fill RegisteredParaIds to its maximum + for i in 0..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + storage.clone(), + ) + .unwrap(); + T::RegistrarHooks::benchmarks_ensure_valid_for_collating(i.into()); + Pallet::::mark_valid_for_collating(RawOrigin::Root.into(), i.into()).unwrap(); + } + + // Second loop to fill Paused to its maximum + for k in 1000..(1000 + y) { + let (caller, _deposit_amount) = + create_funded_user::("caller", k, T::DepositAmount::get()); + Pallet::::register( + RawOrigin::Signed(caller.clone()).into(), + k.into(), + storage.clone(), + ) + .unwrap(); + T::RegistrarHooks::benchmarks_ensure_valid_for_collating(k.into()); + Pallet::::mark_valid_for_collating(RawOrigin::Root.into(), k.into()).unwrap(); + Pallet::::pause_container_chain(RawOrigin::Root.into(), k.into()).unwrap(); + } + + // Check PendingPaused has a length of y + assert_eq!(Pallet::::pending_paused()[0].1.len(), y as usize); + // Check 1000 is in PendingPaused + assert!(Pallet::::pending_paused()[0] + .1 + .contains(&ParaId::from(1000))); + // Check 1000 is not in pending_registered_para_ids + assert!(!Pallet::::pending_registered_para_ids()[0] + .1 + .contains(&ParaId::from(1000))); + + #[extrinsic_call] + Pallet::::unpause_container_chain(RawOrigin::Root, 1000u32.into()); + + // Start a new session + Pallet::::initializer_on_new_session(&T::SessionDelay::get()); + + // Check 1000 is not in Paused + assert!(!Pallet::::paused().contains(&ParaId::from(1000))); + // Check 1000 is in registered_para_ids + assert!(Pallet::::registered_para_ids().contains(&ParaId::from(1000))); + } + + #[benchmark] + fn register_parathread(x: Linear<5, 3_000_000>, y: Linear<1, 50>, z: Linear<1, 10>) { + let mut data = vec![]; + // Number of keys + for _i in 1..z { + data.push((b"code".to_vec(), vec![1; (x / z) as usize]).into()) + } + + let slot_frequency = SlotFrequency::default(); + let storage = new_genesis_data(data); + + for i in 1..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register_parathread( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + slot_frequency.clone(), + storage.clone(), + ) + .unwrap(); + } + + // We should have registered y-1 + assert_eq!(pending_verification_len::(), (y - 1) as usize); + + let (caller, _deposit_amount) = + create_funded_user::("caller", 0, T::DepositAmount::get()); + + #[extrinsic_call] + Pallet::::register_parathread( + RawOrigin::Signed(caller), + Default::default(), + slot_frequency, + storage, + ); + + // verification code + assert_eq!(pending_verification_len::(), y as usize); + assert!(Pallet::::registrar_deposit(ParaId::default()).is_some()); + } + + #[benchmark] + fn set_parathread_params(y: Linear<1, 50>) { + let storage = vec![(vec![1; 4], vec![1; 3_000_000usize]).into()]; + let storage = new_genesis_data(storage); + let slot_frequency = SlotFrequency::default(); + + // Deregister all the existing chains to avoid conflicts with the new ones + for para_id in Pallet::::registered_para_ids() { + Pallet::::deregister(RawOrigin::Root.into(), para_id).unwrap(); + } + + for i in 0..y { + // Twice the deposit just in case + let (caller, _deposit_amount) = + create_funded_user::("caller", i, T::DepositAmount::get()); + Pallet::::register_parathread( + RawOrigin::Signed(caller.clone()).into(), + i.into(), + slot_frequency.clone(), + storage.clone(), + ) + .unwrap(); + T::RegistrarHooks::benchmarks_ensure_valid_for_collating(i.into()); + Pallet::::mark_valid_for_collating(RawOrigin::Root.into(), i.into()).unwrap(); + } + + let new_slot_frequency = SlotFrequency { min: 2, max: 2 }; + + #[extrinsic_call] + Pallet::::set_parathread_params( + RawOrigin::Root, + (y - 1).into(), + new_slot_frequency.clone(), + ); + + // Start a new session + Pallet::::initializer_on_new_session(&T::SessionDelay::get()); + + // Check y-1 has new slot frequency + assert_eq!( + Pallet::::parathread_params(ParaId::from(y - 1)).map(|x| x.slot_frequency), + Some(new_slot_frequency) + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/registrar/src/lib.rs b/pallets/registrar/src/lib.rs new file mode 100644 index 0000000..4a0b6df --- /dev/null +++ b/pallets/registrar/src/lib.rs @@ -0,0 +1,1142 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Registrar Pallet +//! +//! This pallet is in charge of registering containerChains (identified by their Id) +//! that have to be served by the orchestrator chain. Parachains registrations and de- +//! registrations are not immediatly applied, but rather they take T::SessionDelay sessions +//! to be applied. +//! +//! Registered container chains are stored in the PendingParaIds storage item until the session +//! in which they can be onboarded arrives, in which case they are added to the RegisteredParaIds +//! storage item. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(any(test, feature = "runtime-benchmarks"))] +mod benchmarks; +pub mod weights; +pub use weights::WeightInfo; + +pub use pallet::*; + +use { + frame_support::{ + pallet_prelude::*, + traits::{Currency, ReservableCurrency}, + DefaultNoBound, LOG_TARGET, + }, + frame_system::pallet_prelude::*, + parity_scale_codec::{Decode, Encode}, + sp_runtime::{traits::AtLeast32BitUnsigned, Saturating}, + sp_std::{collections::btree_set::BTreeSet, prelude::*}, + tp_container_chain_genesis_data::ContainerChainGenesisData, + tp_traits::{ + GetCurrentContainerChains, GetSessionContainerChains, GetSessionIndex, ParaId, + ParathreadParams as ParathreadParamsTy, SlotFrequency, + }, +}; + +#[frame_support::pallet] +pub mod pallet { + use {super::*, tp_traits::SessionContainerChains}; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig { + /// Para ids + pub para_ids: Vec<(ParaId, ContainerChainGenesisData)>, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + // Sort para ids and detect duplicates, but do it using a vector of + // references to avoid cloning the genesis data, which may be big. + let mut para_ids: Vec<&_> = self.para_ids.iter().collect(); + para_ids.sort_by(|a, b| a.0.cmp(&b.0)); + para_ids.dedup_by(|a, b| { + if a.0 == b.0 { + panic!("Duplicate para_id: {}", u32::from(a.0)); + } else { + false + } + }); + + let mut bounded_para_ids = BoundedVec::default(); + + for (para_id, genesis_data) in para_ids { + bounded_para_ids + .try_push(*para_id) + .expect("too many para ids in genesis: bounded vec full"); + + let genesis_data_size = genesis_data.encoded_size(); + if genesis_data_size > T::MaxGenesisDataSize::get() as usize { + panic!( + "genesis data for para_id {:?} is too large: {} bytes (limit is {})", + u32::from(*para_id), + genesis_data_size, + T::MaxGenesisDataSize::get() + ); + } + >::insert(para_id, genesis_data); + } + + >::put(bounded_para_ids); + } + } + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Origin that is allowed to call register and deregister + type RegistrarOrigin: EnsureOrigin; + + /// Max length of para id list + #[pallet::constant] + type MaxLengthParaIds: Get; + + /// Max length of encoded genesis data + #[pallet::constant] + type MaxGenesisDataSize: Get; + + #[pallet::constant] + type MaxLengthTokenSymbol: Get; + + type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned; + + #[pallet::constant] + type SessionDelay: Get; + + type CurrentSessionIndex: GetSessionIndex; + + type Currency: ReservableCurrency; + + #[pallet::constant] + type DepositAmount: Get<>::Balance>; + + type RegistrarHooks: RegistrarHooks; + + type WeightInfo: WeightInfo; + } + + #[pallet::storage] + #[pallet::getter(fn registered_para_ids)] + pub type RegisteredParaIds = + StorageValue<_, BoundedVec, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn pending_registered_para_ids)] + pub type PendingParaIds = StorageValue< + _, + Vec<(T::SessionIndex, BoundedVec)>, + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn para_genesis_data)] + pub type ParaGenesisData = StorageMap< + _, + Blake2_128Concat, + ParaId, + ContainerChainGenesisData, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn pending_verification)] + pub type PendingVerification = + StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn paused)] + pub type Paused = + StorageValue<_, BoundedVec, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn pending_paused)] + pub type PendingPaused = StorageValue< + _, + Vec<(T::SessionIndex, BoundedVec)>, + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn pending_to_remove)] + pub type PendingToRemove = StorageValue< + _, + Vec<(T::SessionIndex, BoundedVec)>, + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn parathread_params)] + pub type ParathreadParams = + StorageMap<_, Blake2_128Concat, ParaId, ParathreadParamsTy, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn pending_parathread_params)] + pub type PendingParathreadParams = StorageValue< + _, + Vec<( + T::SessionIndex, + BoundedVec<(ParaId, ParathreadParamsTy), T::MaxLengthParaIds>, + )>, + ValueQuery, + >; + + pub type DepositBalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + + #[derive(Default, Clone, Encode, Decode, RuntimeDebug, PartialEq, scale_info::TypeInfo)] + #[scale_info(skip_type_params(T))] + pub struct DepositInfo { + pub creator: T::AccountId, + pub deposit: DepositBalanceOf, + } + + /// Registrar deposits, a mapping from paraId to a struct + /// holding the creator (from which the deposit was reserved) and + /// the deposit amount + #[pallet::storage] + #[pallet::getter(fn registrar_deposit)] + pub type RegistrarDeposit = StorageMap<_, Blake2_128Concat, ParaId, DepositInfo>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A new para id has been registered. [para_id] + ParaIdRegistered { para_id: ParaId }, + /// A para id has been deregistered. [para_id] + ParaIdDeregistered { para_id: ParaId }, + /// A new para id is now valid for collating. [para_id] + ParaIdValidForCollating { para_id: ParaId }, + /// A para id has been paused from collating. + ParaIdPaused { para_id: ParaId }, + /// A para id has been unpaused. + ParaIdUnpaused { para_id: ParaId }, + /// Parathread params changed + ParathreadParamsChanged { para_id: ParaId }, + } + + #[pallet::error] + pub enum Error { + /// Attempted to register a ParaId that was already registered + ParaIdAlreadyRegistered, + /// Attempted to deregister a ParaId that is not registered + ParaIdNotRegistered, + /// Attempted to deregister a ParaId that is already being deregistered + ParaIdAlreadyDeregistered, + /// Attempted to pause a ParaId that was already paused + ParaIdAlreadyPaused, + /// Attempted to unpause a ParaId that was not paused + ParaIdNotPaused, + /// The bounded list of ParaIds has reached its limit + ParaIdListFull, + /// Attempted to register a ParaId with a genesis data size greater than the limit + GenesisDataTooBig, + /// Tried to mark_valid_for_collating a ParaId that is not in PendingVerification + ParaIdNotInPendingVerification, + /// Tried to register a ParaId with an account that did not have enough balance for the deposit + NotSufficientDeposit, + /// Tried to change parathread params for a para id that is not a registered parathread + NotAParathread, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + use {scale_info::prelude::format, sp_std::collections::btree_set::BTreeSet}; + // A para id can only be in 1 of [`RegisteredParaIds`, `PendingVerification`, `Paused`] + // Get all those para ids and check for duplicates + let mut para_ids: Vec = vec![]; + para_ids.extend(RegisteredParaIds::::get()); + para_ids.extend(PendingVerification::::iter_keys()); + para_ids.extend(Paused::::get()); + para_ids.sort(); + para_ids.dedup_by(|a, b| { + if a == b { + panic!("Duplicate para id: {}", u32::from(*a)); + } else { + false + } + }); + + // All para ids have an entry in `ParaGenesisData` + for para_id in ¶_ids { + assert!( + ParaGenesisData::::contains_key(para_id), + "Para id {} missing genesis data", + u32::from(*para_id) + ); + } + + // All entries in `RegistrarDeposit` and `ParaGenesisData` are in one of the other lists + let mut para_id_set = BTreeSet::from_iter(para_ids.iter().cloned()); + // Also add the Pending lists here + para_id_set.extend( + PendingParaIds::::get() + .into_iter() + .flat_map(|(_session_index, x)| x), + ); + para_id_set.extend( + PendingPaused::::get() + .into_iter() + .flat_map(|(_session_index, x)| x), + ); + para_id_set.extend( + PendingToRemove::::get() + .into_iter() + .flat_map(|(_session_index, x)| x), + ); + let entries: Vec<_> = RegistrarDeposit::::iter().map(|(k, _v)| k).collect(); + for para_id in entries { + assert!( + para_id_set.contains(¶_id), + "Found RegistrarDeposit for unknown para id: {}", + u32::from(para_id) + ); + } + let entries: Vec<_> = ParaGenesisData::::iter().map(|(k, _v)| k).collect(); + for para_id in entries { + assert!( + para_id_set.contains(¶_id), + "Found ParaGenesisData for unknown para id: {}", + u32::from(para_id) + ); + } + + // Sorted storage items are sorted + fn assert_is_sorted_and_unique(x: &[T], name: &str) { + assert!( + x.windows(2).all(|w| w[0] < w[1]), + "sorted list not sorted or not unique: {}", + name, + ); + } + assert_is_sorted_and_unique(&RegisteredParaIds::::get(), "RegisteredParaIds"); + assert_is_sorted_and_unique(&Paused::::get(), "Paused"); + for (i, (_session_index, x)) in PendingParaIds::::get().into_iter().enumerate() { + assert_is_sorted_and_unique(&x, &format!("PendingParaIds[{}]", i)); + } + for (i, (_session_index, x)) in PendingPaused::::get().into_iter().enumerate() { + assert_is_sorted_and_unique(&x, &format!("PendingPaused[{}]", i)); + } + for (i, (_session_index, x)) in PendingToRemove::::get().into_iter().enumerate() { + assert_is_sorted_and_unique(&x, &format!("PendingToRemove[{}]", i)); + } + + // Pending storage items are sorted and session index is unique + let pending: Vec<_> = PendingParaIds::::get() + .into_iter() + .map(|(session_index, _x)| session_index) + .collect(); + assert_is_sorted_and_unique(&pending, "PendingParaIds"); + let pending: Vec<_> = PendingPaused::::get() + .into_iter() + .map(|(session_index, _x)| session_index) + .collect(); + assert_is_sorted_and_unique(&pending, "PendingPaused"); + let pending: Vec<_> = PendingToRemove::::get() + .into_iter() + .map(|(session_index, _x)| session_index) + .collect(); + assert_is_sorted_and_unique(&pending, "PendingToRemove"); + + Ok(()) + } + } + + #[pallet::call] + impl Pallet { + /// Register container-chain + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::register(genesis_data.encoded_size() as u32, T::MaxLengthParaIds::get(), genesis_data.storage.len() as u32))] + pub fn register( + origin: OriginFor, + para_id: ParaId, + genesis_data: ContainerChainGenesisData, + ) -> DispatchResult { + let account = ensure_signed(origin)?; + Self::do_register(account, para_id, genesis_data)?; + Self::deposit_event(Event::ParaIdRegistered { para_id }); + + Ok(()) + } + + /// Deregister container-chain. + /// + /// If a container-chain is registered but not marked as valid_for_collating, this will remove it + /// from `PendingVerification` as well. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::deregister_immediate( + T::MaxGenesisDataSize::get(), + T::MaxLengthParaIds::get() + ).max(T::WeightInfo::deregister_scheduled( + T::MaxGenesisDataSize::get(), + T::MaxLengthParaIds::get() + )))] + pub fn deregister(origin: OriginFor, para_id: ParaId) -> DispatchResult { + T::RegistrarOrigin::ensure_origin(origin)?; + + // Check if the para id is in "PendingVerification". + // This is a special case because then we can remove it immediately, instead of waiting 2 sessions. + let is_pending_verification = PendingVerification::::take(para_id).is_some(); + if is_pending_verification { + Self::deposit_event(Event::ParaIdDeregistered { para_id }); + // Cleanup immediately + Self::cleanup_deregistered_para_id(para_id); + } else { + Self::schedule_paused_parachain_change(|para_ids, paused| { + // We have to find out where, in the sorted vec the para id is, if anywhere. + + match para_ids.binary_search(¶_id) { + Ok(index) => { + para_ids.remove(index); + } + Err(_) => { + // If the para id is not registered, it may be paused. In that case, remove it from there + match paused.binary_search(¶_id) { + Ok(index) => { + paused.remove(index); + } + Err(_) => { + return Err(Error::::ParaIdNotRegistered.into()); + } + } + } + } + + Ok(()) + })?; + // Mark this para id for cleanup later + Self::schedule_parachain_cleanup(para_id)?; + Self::deposit_event(Event::ParaIdDeregistered { para_id }); + } + + Ok(()) + } + + /// Mark container-chain valid for collating + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::mark_valid_for_collating(T::MaxLengthParaIds::get()))] + pub fn mark_valid_for_collating(origin: OriginFor, para_id: ParaId) -> DispatchResult { + T::RegistrarOrigin::ensure_origin(origin)?; + + let is_pending_verification = PendingVerification::::take(para_id).is_some(); + if !is_pending_verification { + return Err(Error::::ParaIdNotInPendingVerification.into()); + } + + Self::schedule_parachain_change(|para_ids| { + // We don't want to add duplicate para ids, so we check whether the potential new + // para id is already present in the list. Because the list is always ordered, we can + // leverage the binary search which makes this check O(log n). + + match para_ids.binary_search(¶_id) { + // This Ok is unreachable + Ok(_) => return Err(Error::::ParaIdAlreadyRegistered.into()), + Err(index) => { + para_ids + .try_insert(index, para_id) + .map_err(|_e| Error::::ParaIdListFull)?; + } + } + + Ok(()) + })?; + + T::RegistrarHooks::check_valid_for_collating(para_id)?; + + Self::deposit_event(Event::ParaIdValidForCollating { para_id }); + + T::RegistrarHooks::para_marked_valid_for_collating(para_id); + + Ok(()) + } + + /// Pause container-chain from collating. Does not remove its boot nodes nor its genesis config. + /// Only container-chains that have been marked as valid_for_collating can be paused. + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::pause_container_chain(T::MaxLengthParaIds::get()))] + pub fn pause_container_chain(origin: OriginFor, para_id: ParaId) -> DispatchResult { + T::RegistrarOrigin::ensure_origin(origin)?; + + Self::schedule_paused_parachain_change(|para_ids, paused| { + match paused.binary_search(¶_id) { + Ok(_) => return Err(Error::::ParaIdAlreadyPaused.into()), + Err(index) => { + paused + .try_insert(index, para_id) + .map_err(|_e| Error::::ParaIdListFull)?; + } + } + match para_ids.binary_search(¶_id) { + Ok(index) => { + para_ids.remove(index); + } + // We can only pause para ids that are marked as valid, + // otherwise unpausing them later would cause problems + Err(_) => return Err(Error::::ParaIdNotRegistered.into()), + } + Self::deposit_event(Event::ParaIdPaused { para_id }); + + Ok(()) + })?; + + Ok(()) + } + + /// Unpause container-chain. + /// Only container-chains that have been paused can be unpaused. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::unpause_container_chain(T::MaxLengthParaIds::get()))] + pub fn unpause_container_chain(origin: OriginFor, para_id: ParaId) -> DispatchResult { + T::RegistrarOrigin::ensure_origin(origin)?; + + Self::schedule_paused_parachain_change(|para_ids, paused| { + match paused.binary_search(¶_id) { + Ok(index) => { + paused.remove(index); + } + Err(_) => return Err(Error::::ParaIdNotPaused.into()), + } + match para_ids.binary_search(¶_id) { + // This Ok is unreachable, a para id cannot be in "RegisteredParaIds" and "Paused" at the same time + Ok(_) => return Err(Error::::ParaIdAlreadyRegistered.into()), + Err(index) => { + para_ids + .try_insert(index, para_id) + .map_err(|_e| Error::::ParaIdListFull)?; + } + } + Self::deposit_event(Event::ParaIdUnpaused { para_id }); + + Ok(()) + })?; + + Ok(()) + } + + /// Register parathread + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::register_parathread(genesis_data.encoded_size() as u32, T::MaxLengthParaIds::get(), genesis_data.storage.len() as u32))] + pub fn register_parathread( + origin: OriginFor, + para_id: ParaId, + slot_frequency: SlotFrequency, + genesis_data: ContainerChainGenesisData, + ) -> DispatchResult { + let account = ensure_signed(origin)?; + Self::do_register(account, para_id, genesis_data)?; + // Insert parathread params + let params = ParathreadParamsTy { slot_frequency }; + ParathreadParams::::insert(para_id, params); + Self::deposit_event(Event::ParaIdRegistered { para_id }); + + Ok(()) + } + + /// Change parathread params + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::set_parathread_params(T::MaxLengthParaIds::get()))] + pub fn set_parathread_params( + origin: OriginFor, + para_id: ParaId, + slot_frequency: SlotFrequency, + ) -> DispatchResult { + T::RegistrarOrigin::ensure_origin(origin)?; + + Self::schedule_parathread_params_change(para_id, |params| { + params.slot_frequency = slot_frequency; + + Self::deposit_event(Event::ParathreadParamsChanged { para_id }); + + Ok(()) + })?; + + Ok(()) + } + } + + pub struct SessionChangeOutcome { + /// Previously active parachains. + pub prev_paras: BoundedVec, + /// If new parachains have been applied in the new session, this is the new list. + pub new_paras: Option>, + } + + impl Pallet { + pub fn is_para_manager(para_id: &ParaId, account: &T::AccountId) -> bool { + // This check will only pass if both are true: + // * The para_id has a deposit in pallet_registrar + // * The deposit creator is the signed_account + RegistrarDeposit::::get(para_id) + .map(|deposit_info| deposit_info.creator) + .as_ref() + == Some(account) + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn benchmarks_get_or_create_para_manager(para_id: &ParaId) -> T::AccountId { + use { + frame_benchmarking::account, + frame_support::{assert_ok, dispatch::RawOrigin, traits::Currency}, + }; + // Return container chain manager, or register container chain as ALICE if it does not exist + if !ParaGenesisData::::contains_key(para_id) { + // Register as a new user + + /// Create a funded user. + /// Used for generating the necessary amount for registering + fn create_funded_user( + string: &'static str, + n: u32, + total: DepositBalanceOf, + ) -> (T::AccountId, DepositBalanceOf) { + const SEED: u32 = 0; + let user = account(string, n, SEED); + T::Currency::make_free_balance_be(&user, total); + let _ = T::Currency::issue(total); + (user, total) + } + let new_balance = + (T::Currency::minimum_balance() + T::DepositAmount::get()) * 2u32.into(); + let account = create_funded_user::("caller", 1000, new_balance).0; + let origin = RawOrigin::Signed(account); + assert_ok!(Self::register(origin.into(), *para_id, Default::default())); + } + + let deposit_info = RegistrarDeposit::::get(para_id).expect("Cannot return signed origin for a container chain that was registered by root. Try using a different para id"); + + // Fund deposit creator, just in case it is not a new account + let new_balance = + (T::Currency::minimum_balance() + T::DepositAmount::get()) * 2u32.into(); + T::Currency::make_free_balance_be(&deposit_info.creator, new_balance); + let _ = T::Currency::issue(new_balance); + + deposit_info.creator + } + + fn do_register( + account: T::AccountId, + para_id: ParaId, + genesis_data: ContainerChainGenesisData, + ) -> DispatchResult { + let deposit = T::DepositAmount::get(); + + // Verify we can reserve + T::Currency::can_reserve(&account, deposit) + .then_some(true) + .ok_or(Error::::NotSufficientDeposit)?; + + // Check if the para id is already registered by looking at the genesis data + if ParaGenesisData::::contains_key(para_id) { + return Err(Error::::ParaIdAlreadyRegistered.into()); + } + + // Check if the para id is already in PendingVerification (unreachable) + let is_pending_verification = PendingVerification::::take(para_id).is_some(); + if is_pending_verification { + return Err(Error::::ParaIdAlreadyRegistered.into()); + } + + // Insert para id into PendingVerification + PendingVerification::::insert(para_id, ()); + + // The actual registration takes place 2 sessions after the call to + // `mark_valid_for_collating`, but the genesis data is inserted now. + // This is because collators should be able to start syncing the new container chain + // before the first block is mined. However, we could store the genesis data in a + // different key, like PendingParaGenesisData. + // TODO: for benchmarks, this call to .encoded_size is O(n) with respect to the number + // of key-values in `genesis_data.storage`, even if those key-values are empty. And we + // won't detect that the size is too big until after iterating over all of them, so the + // limit in that case would be the transaction size. + let genesis_data_size = genesis_data.encoded_size(); + if genesis_data_size > T::MaxGenesisDataSize::get() as usize { + return Err(Error::::GenesisDataTooBig.into()); + } + + // Reserve the deposit, we verified we can do this + T::Currency::reserve(&account, deposit)?; + + // Update DepositInfo + RegistrarDeposit::::insert( + para_id, + DepositInfo { + creator: account, + deposit, + }, + ); + ParaGenesisData::::insert(para_id, genesis_data); + + Ok(()) + } + + fn schedule_parachain_change( + updater: impl FnOnce(&mut BoundedVec) -> DispatchResult, + ) -> DispatchResult { + let mut pending_paras = PendingParaIds::::get(); + // First, we need to decide what we should use as the base paras. + let mut base_paras = pending_paras + .last() + .map(|(_, paras)| paras.clone()) + .unwrap_or_else(Self::registered_para_ids); + + updater(&mut base_paras)?; + let new_paras = base_paras; + + let scheduled_session = Self::scheduled_session(); + + if let Some(&mut (_, ref mut paras)) = pending_paras + .iter_mut() + .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session) + { + *paras = new_paras; + } else { + // We are scheduling a new parachains change for the scheduled session. + pending_paras.push((scheduled_session, new_paras)); + } + + >::put(pending_paras); + + Ok(()) + } + + fn schedule_paused_parachain_change( + updater: impl FnOnce( + &mut BoundedVec, + &mut BoundedVec, + ) -> DispatchResult, + ) -> DispatchResult { + let mut pending_paras = PendingParaIds::::get(); + let mut pending_paused = PendingPaused::::get(); + // First, we need to decide what we should use as the base paras. + let mut base_paras = pending_paras + .last() + .map(|(_, paras)| paras.clone()) + .unwrap_or_else(Self::registered_para_ids); + let mut base_paused = pending_paused + .last() + .map(|(_, paras)| paras.clone()) + .unwrap_or_else(Self::paused); + let old_base_paras = base_paras.clone(); + let old_base_paused = base_paused.clone(); + + updater(&mut base_paras, &mut base_paused)?; + + if base_paras != old_base_paras { + let new_paras = base_paras; + let scheduled_session = Self::scheduled_session(); + + if let Some(&mut (_, ref mut paras)) = pending_paras + .iter_mut() + .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session) + { + *paras = new_paras; + } else { + // We are scheduling a new parachains change for the scheduled session. + pending_paras.push((scheduled_session, new_paras)); + } + + >::put(pending_paras); + } + + if base_paused != old_base_paused { + let new_paused = base_paused; + let scheduled_session = Self::scheduled_session(); + + if let Some(&mut (_, ref mut paras)) = pending_paused + .iter_mut() + .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session) + { + *paras = new_paused; + } else { + // We are scheduling a new parachains change for the scheduled session. + pending_paused.push((scheduled_session, new_paused)); + } + + >::put(pending_paused); + } + + Ok(()) + } + + fn schedule_parathread_params_change( + para_id: ParaId, + updater: impl FnOnce(&mut ParathreadParamsTy) -> DispatchResult, + ) -> DispatchResult { + // Check that the para id is a parathread by reading the old params + let params = match ParathreadParams::::get(para_id) { + Some(x) => x, + None => { + return Err(Error::::NotAParathread.into()); + } + }; + + let mut pending_params = PendingParathreadParams::::get(); + // First, we need to decide what we should use as the base params. + let mut base_params = pending_params + .last() + .and_then(|(_, para_id_params)| { + match para_id_params + .binary_search_by_key(¶_id, |(para_id, _params)| *para_id) + { + Ok(idx) => { + let (_para_id, params) = ¶_id_params[idx]; + Some(params.clone()) + } + Err(_idx) => None, + } + }) + .unwrap_or(params); + + updater(&mut base_params)?; + let new_params = base_params; + + let scheduled_session = Self::scheduled_session(); + + if let Some(&mut (_, ref mut para_id_params)) = pending_params + .iter_mut() + .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session) + { + match para_id_params.binary_search_by_key(¶_id, |(para_id, _params)| *para_id) { + Ok(idx) => { + let (_para_id, params) = &mut para_id_params[idx]; + *params = new_params; + } + Err(idx) => { + para_id_params + .try_insert(idx, (para_id, new_params)) + .map_err(|_e| Error::::ParaIdListFull)?; + } + } + } else { + // We are scheduling a new parathread params change for the scheduled session. + pending_params.push(( + scheduled_session, + BoundedVec::truncate_from(vec![(para_id, new_params)]), + )); + } + + >::put(pending_params); + + Ok(()) + } + + /// Return the session index that should be used for any future scheduled changes. + fn scheduled_session() -> T::SessionIndex { + T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get()) + } + + /// Called by the initializer to note that a new session has started. + /// + /// Returns the parachain list that was actual before the session change and the parachain list + /// that became active after the session change. If there were no scheduled changes, both will + /// be the same. + pub fn initializer_on_new_session( + session_index: &T::SessionIndex, + ) -> SessionChangeOutcome { + let pending_paras = >::get(); + let prev_paras = RegisteredParaIds::::get(); + + let new_paras = if !pending_paras.is_empty() { + let (mut past_and_present, future) = pending_paras + .into_iter() + .partition::, _>(|&(apply_at_session, _)| { + apply_at_session <= *session_index + }); + + if past_and_present.len() > 1 { + // This should never happen since we schedule parachain changes only into the future + // sessions and this handler called for each session change. + log::error!( + target: LOG_TARGET, + "Skipping applying parachain changes scheduled sessions in the past", + ); + } + + let new_paras = past_and_present.pop().map(|(_, paras)| paras); + if let Some(ref new_paras) = new_paras { + // Apply the new parachain list. + RegisteredParaIds::::put(new_paras); + >::put(future); + } + + new_paras + } else { + // pending_paras.is_empty, so parachain list did not change + None + }; + + let pending_paused = >::get(); + if !pending_paused.is_empty() { + let (mut past_and_present, future) = pending_paused + .into_iter() + .partition::, _>(|&(apply_at_session, _)| { + apply_at_session <= *session_index + }); + + if past_and_present.len() > 1 { + // This should never happen since we schedule parachain changes only into the future + // sessions and this handler called for each session change. + log::error!( + target: LOG_TARGET, + "Skipping applying paused parachain changes scheduled sessions in the past", + ); + } + + let new_paused = past_and_present.pop().map(|(_, paras)| paras); + if let Some(ref new_paused) = new_paused { + // Apply the new parachain list. + Paused::::put(new_paused); + >::put(future); + } + } + + let pending_parathread_params = >::get(); + if !pending_parathread_params.is_empty() { + let (mut past_and_present, future) = pending_parathread_params + .into_iter() + .partition::, _>(|&(apply_at_session, _)| { + apply_at_session <= *session_index + }); + + if past_and_present.len() > 1 { + // This should never happen since we schedule parachain changes only into the future + // sessions and this handler called for each session change. + log::error!( + target: LOG_TARGET, + "Skipping applying parathread params changes scheduled sessions in the past", + ); + } + + let new_params = past_and_present.pop().map(|(_, params)| params); + if let Some(ref new_params) = new_params { + for (para_id, params) in new_params { + >::insert(para_id, params); + } + >::put(future); + } + } + + let pending_to_remove = >::get(); + if !pending_to_remove.is_empty() { + let (past_and_present, future) = + pending_to_remove.into_iter().partition::, _>( + |&(apply_at_session, _)| apply_at_session <= *session_index, + ); + + if !past_and_present.is_empty() { + // Unlike `PendingParaIds`, this cannot skip items because we must cleanup all parachains. + // But this will only happen if `initializer_on_new_session` is not called for a big range of + // sessions, and many parachains are deregistered in the meantime. + let mut removed_para_ids = BTreeSet::new(); + for (_, new_paras) in &past_and_present { + for para_id in new_paras { + Self::cleanup_deregistered_para_id(*para_id); + removed_para_ids.insert(*para_id); + } + } + + // Also need to remove PendingParams to avoid setting params for a para id that does not exist + let mut pending_parathread_params = >::get(); + for (_, new_params) in &mut pending_parathread_params { + new_params.retain(|(para_id, _params)| { + // Retain para ids that are not in the list of removed para ids + !removed_para_ids.contains(para_id) + }); + } + >::put(pending_parathread_params); + >::put(future); + } + } + + SessionChangeOutcome { + prev_paras, + new_paras, + } + } + + /// Remove all para id storage in this pallet, + /// and execute para_deregistered hook to clean up other pallets as well + fn cleanup_deregistered_para_id(para_id: ParaId) { + ParaGenesisData::::remove(para_id); + ParathreadParams::::remove(para_id); + // Get asset creator and deposit amount + // Deposit may not exist, for example if the para id was registered on genesis + if let Some(asset_info) = RegistrarDeposit::::take(para_id) { + // Unreserve deposit + T::Currency::unreserve(&asset_info.creator, asset_info.deposit); + } + + T::RegistrarHooks::para_deregistered(para_id); + } + + fn schedule_parachain_cleanup(para_id: ParaId) -> DispatchResult { + let scheduled_session = Self::scheduled_session(); + let mut pending_paras = PendingToRemove::::get(); + // First, we need to decide what we should use as the base paras. + let base_paras = match pending_paras + .binary_search_by_key(&scheduled_session, |(session, _paras)| *session) + { + Ok(i) => &mut pending_paras[i].1, + Err(i) => { + pending_paras.insert(i, (scheduled_session, Default::default())); + + &mut pending_paras[i].1 + } + }; + + // Add the para_id to the entry for the scheduled session. + match base_paras.binary_search(¶_id) { + // This Ok is unreachable + Ok(_) => return Err(Error::::ParaIdAlreadyDeregistered.into()), + Err(index) => { + base_paras + .try_insert(index, para_id) + .map_err(|_e| Error::::ParaIdListFull)?; + } + } + + // Save the updated list of pending parachains for removal. + >::put(pending_paras); + + Ok(()) + } + } + + impl GetCurrentContainerChains for Pallet { + type MaxContainerChains = T::MaxLengthParaIds; + + fn current_container_chains() -> BoundedVec { + Self::registered_para_ids() + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_current_container_chains(container_chains: &[ParaId]) { + let paras: BoundedVec = + container_chains.to_vec().try_into().unwrap(); + RegisteredParaIds::::put(paras); + } + } + + impl GetSessionContainerChains for Pallet { + fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains { + let (past_and_present, _) = Pallet::::pending_registered_para_ids() + .into_iter() + .partition::, _>(|&(apply_at_session, _)| apply_at_session <= session_index); + + let paras = if let Some(last) = past_and_present.last() { + last.1.clone() + } else { + Pallet::::registered_para_ids() + }; + + let mut parachains = vec![]; + let mut parathreads = vec![]; + + for para_id in paras { + // TODO: sweet O(n) db reads + if let Some(parathread_params) = ParathreadParams::::get(para_id) { + parathreads.push((para_id, parathread_params)); + } else { + parachains.push(para_id); + } + } + + SessionContainerChains { + parachains, + parathreads, + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_session_container_chains( + _session_index: T::SessionIndex, + container_chains: &[ParaId], + ) { + // TODO: this assumes session_index == current + let paras: BoundedVec = + container_chains.to_vec().try_into().unwrap(); + RegisteredParaIds::::put(paras); + } + } +} + +pub trait RegistrarHooks { + fn para_marked_valid_for_collating(_para_id: ParaId) -> Weight { + Weight::default() + } + fn para_deregistered(_para_id: ParaId) -> Weight { + Weight::default() + } + fn check_valid_for_collating(_para_id: ParaId) -> DispatchResult { + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn benchmarks_ensure_valid_for_collating(_para_id: ParaId) {} +} + +impl RegistrarHooks for () {} + +pub struct EnsureSignedByManager(sp_std::marker::PhantomData); + +impl frame_support::traits::EnsureOriginWithArg + for EnsureSignedByManager +where + T: Config, +{ + type Success = (); + + fn try_origin( + o: T::RuntimeOrigin, + para_id: &ParaId, + ) -> Result { + let signed_account = + as EnsureOrigin<_>>::try_origin(o.clone())?; + + if !Pallet::::is_para_manager(para_id, &signed_account) { + return Err(frame_system::RawOrigin::Signed(signed_account).into()); + } + + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(para_id: &ParaId) -> Result { + let manager = Pallet::::benchmarks_get_or_create_para_manager(para_id); + + Ok(frame_system::RawOrigin::Signed(manager).into()) + } +} diff --git a/pallets/registrar/src/mock.rs b/pallets/registrar/src/mock.rs new file mode 100644 index 0000000..a0ef533 --- /dev/null +++ b/pallets/registrar/src/mock.rs @@ -0,0 +1,311 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use tp_container_chain_genesis_data::ContainerChainGenesisData; + +use { + crate::{self as pallet_registrar, RegistrarHooks}, + frame_support::{ + traits::{ConstU16, ConstU64}, + weights::Weight, + }, + parity_scale_codec::{Decode, Encode}, + sp_core::{parameter_types, ConstU32, H256}, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, + }, + std::collections::BTreeMap, + tp_traits::ParaId, +}; + +type Block = frame_system::mocking::MockBlock; +pub type Balance = u128; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + ParaRegistrar: pallet_registrar, + Mock: mock_data, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = (); +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} +impl pallet_balances::Config for Test { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); + type MaxHolds = (); + type WeightInfo = (); +} + +pub struct CurrentSessionIndexGetter; + +impl tp_traits::GetSessionIndex for CurrentSessionIndexGetter { + /// Returns current session index. + fn session_index() -> u32 { + // For tests, let 1 session be 5 blocks + (System::block_number() / 5) as u32 + } +} + +parameter_types! { + pub const DepositAmount: Balance = 100; + pub const MaxLengthTokenSymbol: u32 = 255; +} +impl pallet_registrar::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RegistrarOrigin = frame_system::EnsureRoot; + type MaxLengthParaIds = ConstU32<1000>; + type MaxGenesisDataSize = ConstU32<5_000_000>; + type MaxLengthTokenSymbol = MaxLengthTokenSymbol; + type SessionDelay = ConstU32<2>; + type SessionIndex = u32; + type CurrentSessionIndex = CurrentSessionIndexGetter; + type Currency = Balances; + type DepositAmount = DepositAmount; + type RegistrarHooks = Mock; + type WeightInfo = (); +} + +// Pallet to provide some mock data, used to test +#[frame_support::pallet] +pub mod mock_data { + use {super::*, frame_support::pallet_prelude::*}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn mock)] + pub(super) type Mock = StorageValue<_, Mocks, ValueQuery>; + + impl Pallet { + pub fn get() -> Mocks { + Mock::::get() + } + pub fn mutate(f: F) -> R + where + F: FnOnce(&mut Mocks) -> R, + { + Mock::::mutate(f) + } + } +} + +#[derive(Clone, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum HookCall { + MarkedValid(ParaId), + Deregistered(ParaId), +} + +pub enum HookCallType { + MarkedValid, + Deregistered, +} + +// We use the mock_data pallet to test registrar hooks: we store a list of all the calls, and then check that there +// are no consecutive calls. Because there used to be a bug where the deregister hook was called twice. +impl RegistrarHooks for mock_data::Pallet { + fn para_deregistered(para_id: ParaId) -> Weight { + Mock::mutate(|m| { + m.called_hooks.push(HookCall::Deregistered(para_id)); + + Weight::default() + }) + } + + fn para_marked_valid_for_collating(para_id: ParaId) -> Weight { + Mock::mutate(|m| { + m.called_hooks.push(HookCall::MarkedValid(para_id)); + + Weight::default() + }) + } + + fn check_valid_for_collating(_para_id: ParaId) -> crate::DispatchResult { + // Ignored, we already test this in integration tests + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn benchmarks_ensure_valid_for_collating(_para_id: ParaId) {} +} + +impl mock_data::Config for Test {} + +#[derive( + Clone, Default, Encode, Decode, PartialEq, sp_core::RuntimeDebug, scale_info::TypeInfo, +)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Mocks { + pub called_hooks: Vec, +} + +impl Drop for Mocks { + fn drop(&mut self) { + self.check_consistency(); + } +} + +impl Mocks { + pub fn check_consistency(&self) { + /// Asserts that the calls for each ParaId alternate between MarkedValid and Deregister, + /// we never see two calls with the same type. + pub fn assert_alternating(hook_calls: &[HookCall]) { + let mut last_call_type: BTreeMap = BTreeMap::new(); + + for call in hook_calls { + match call { + HookCall::MarkedValid(para_id) => { + if let Some(HookCallType::MarkedValid) = last_call_type.get(para_id) { + panic!( + "Two consecutive MarkedValid calls for ParaId: {:?}", + para_id + ); + } + last_call_type.insert(*para_id, HookCallType::MarkedValid); + } + HookCall::Deregistered(para_id) => { + if let Some(HookCallType::Deregistered) = last_call_type.get(para_id) { + panic!( + "Two consecutive Deregistered calls for ParaId: {:?}", + para_id + ); + } + last_call_type.insert(*para_id, HookCallType::Deregistered); + } + } + } + } + + // For each para id, the calls must alterante between MarkedValid and Deregister + assert_alternating(&self.called_hooks); + // Since para ids can already be registered in genesis, we cannot assert that the first call is MarkedValid + } +} + +const ALICE: u64 = 1; + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, 1_000)], + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext_with_genesis( + para_ids: Vec<(ParaId, ContainerChainGenesisData)>, +) -> sp_io::TestExternalities { + RuntimeGenesisConfig { + system: Default::default(), + balances: Default::default(), + para_registrar: pallet_registrar::GenesisConfig { para_ids }, + } + .build_storage() + .unwrap() + .into() +} + +pub fn empty_genesis_data() -> ContainerChainGenesisData { + ContainerChainGenesisData { + storage: Default::default(), + name: Default::default(), + id: Default::default(), + fork_id: Default::default(), + extensions: Default::default(), + properties: Default::default(), + } +} + +pub const SESSION_LEN: u64 = 5; + +pub fn run_to_session(n: u32) { + let block_number = SESSION_LEN * u64::from(n); + run_to_block(block_number + 1); +} + +pub fn run_to_block(n: u64) { + let old_block_number = System::block_number(); + + for x in (old_block_number + 1)..=n { + System::reset_events(); + System::set_block_number(x); + + if x % SESSION_LEN == 1 { + let session_index = (x / SESSION_LEN) as u32; + ParaRegistrar::initializer_on_new_session(&session_index); + } + } +} diff --git a/pallets/registrar/src/tests.rs b/pallets/registrar/src/tests.rs new file mode 100644 index 0000000..1e93aa6 --- /dev/null +++ b/pallets/registrar/src/tests.rs @@ -0,0 +1,1370 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{mock::*, Error, Event}, + frame_support::{assert_noop, assert_ok, dispatch::GetDispatchInfo, BoundedVec}, + parity_scale_codec::Encode, + sp_core::Get, + sp_runtime::DispatchError, + tp_container_chain_genesis_data::ContainerChainGenesisData, + tp_traits::{ParaId, SlotFrequency}, +}; + +const ALICE: u64 = 1; +//const BOB: u64 = 2; + +#[test] +fn register_para_id_42() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdRegistered { para_id: 42.into() }.into()); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdValidForCollating { para_id: 42.into() }.into()); + + // Assert after two sessions it goes to the non-pending + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + assert_eq!(ParaRegistrar::pending_registered_para_ids(), vec![]); + }); +} + +#[test] +fn register_para_id_42_twice() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_noop!( + ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + ), + Error::::ParaIdAlreadyRegistered + ); + }); +} + +#[test] +fn register_para_id_42_genesis_data_size_too_big() { + new_test_ext().execute_with(|| { + run_to_block(1); + let genesis_data = ContainerChainGenesisData { + storage: vec![(vec![], vec![0; 5_000_000]).into()], + name: Default::default(), + id: Default::default(), + fork_id: Default::default(), + extensions: Default::default(), + properties: Default::default(), + }; + assert_noop!( + ParaRegistrar::register(RuntimeOrigin::signed(ALICE), 42.into(), genesis_data,), + Error::::GenesisDataTooBig, + ); + }); +} + +#[test] +fn deregister_para_id_from_empty_list() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_noop!( + ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into()), + Error::::ParaIdNotRegistered + ); + }); +} + +#[test] +fn deregister_para_id_42_after_0_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![]).unwrap())] + ); + // This para id will never be in registered so we do not need to keep the genesis data, + // but we do anyway, and the genesis data is deleted after 2 sessions + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(1); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + // Assert after two sessions genesis data gets deleted + run_to_session(2); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn deregister_para_id_42_after_1_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(1); + // Deregister while its pending + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![ + (2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap()), + (3u32, BoundedVec::try_from(vec![]).unwrap()) + ] + ); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(3u32, BoundedVec::try_from(vec![]).unwrap())] + ); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(3); + assert_eq!(ParaRegistrar::pending_registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn deregister_para_id_42_after_2_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + + // Assert after two sessions it goes to the non-pending + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(4u32, BoundedVec::try_from(vec![]).unwrap())] + ); + + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(3); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(4); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn deregister_para_id_42_twice() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![]).unwrap())] + ); + assert_noop!( + ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into()), + Error::::ParaIdNotRegistered + ); + }); +} + +#[test] +fn deregister_para_id_removes_genesis_data() { + new_test_ext().execute_with(|| { + run_to_block(1); + let genesis_data = ContainerChainGenesisData { + storage: vec![(b"key".to_vec(), b"value".to_vec()).into()], + name: Default::default(), + id: Default::default(), + fork_id: Default::default(), + extensions: Default::default(), + properties: Default::default(), + }; + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + genesis_data.clone(), + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + Some(&genesis_data), + ); + + // Assert after two sessions it goes to the non-pending + run_to_session(2); + + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(4u32, BoundedVec::try_from(vec![]).unwrap())] + ); + + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + + // Genesis data has not been deleted yet, it will be deleted after 2 sessions + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)), + Some(genesis_data), + ); + run_to_session(4); + assert_eq!(ParaRegistrar::para_genesis_data(ParaId::from(42)), None); + }); +} + +#[test] +fn register_para_id_bad_origin() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_noop!( + ParaRegistrar::register(RuntimeOrigin::root(), 42.into(), empty_genesis_data()), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn deregister_para_id_bad_origin() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_noop!( + ParaRegistrar::deregister(RuntimeOrigin::signed(1), 42.into()), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn mark_valid_for_collating_bad_origin() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_noop!( + ParaRegistrar::mark_valid_for_collating(RuntimeOrigin::signed(1), 42.into()), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn pause_para_id_42_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + + // Enable the container-chain for the first time + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + + // Pause the container-chain + assert_ok!(ParaRegistrar::pause_container_chain( + RuntimeOrigin::root(), + 42.into(), + )); + + // Assert that the ParaIdPaused event was emitted + System::assert_last_event(Event::ParaIdPaused { para_id: 42.into() }.into()); + + // Check genesis data was not removed + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + // Check the container chain was not selected for the next period + run_to_session(4); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + }); +} + +#[test] +fn pause_para_id_42_twice_fails() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + + // Enable the container-chain for collating + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + + // Pause the container-chain + assert_ok!(ParaRegistrar::pause_container_chain( + RuntimeOrigin::root(), + 42.into(), + )); + + // Try to pause again + assert_noop!( + ParaRegistrar::pause_container_chain(RuntimeOrigin::root(), 42.into()), + Error::::ParaIdAlreadyPaused + ); + }); +} + +#[test] +fn pause_para_id_42_fails_not_registered() { + new_test_ext().execute_with(|| { + run_to_block(1); + // Try to pause + assert_noop!( + ParaRegistrar::pause_container_chain(RuntimeOrigin::root(), 42.into()), + Error::::ParaIdNotRegistered + ); + }); +} + +#[test] +fn pause_container_chain_bad_origin() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_noop!( + ParaRegistrar::pause_container_chain(RuntimeOrigin::signed(1), 42.into()), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn unpause_para_id_that_is_not_paused_fails() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + + // Enable the container-chain for collating + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + + // Try to unpause + assert_noop!( + ParaRegistrar::unpause_container_chain(RuntimeOrigin::root(), 42.into()), + Error::::ParaIdNotPaused + ); + }); +} + +#[test] +fn unpause_para_id_42_twice_fails() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + + // Enable the container-chain for collating + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + ParaRegistrar::pending_registered_para_ids(), + vec![(2u32, BoundedVec::try_from(vec![42u32.into()]).unwrap())] + ); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + + // Pause the container-chain + assert_ok!(ParaRegistrar::pause_container_chain( + RuntimeOrigin::root(), + 42.into(), + )); + + // Unpause + assert_ok!(ParaRegistrar::unpause_container_chain( + RuntimeOrigin::root(), + 42.into(), + ),); + + // Unpause again fails + assert_noop!( + ParaRegistrar::unpause_container_chain(RuntimeOrigin::root(), 42.into()), + Error::::ParaIdNotPaused + ); + }); +} + +#[test] +fn unpause_para_id_42_fails_not_registered() { + new_test_ext().execute_with(|| { + run_to_block(1); + // Try to pause + assert_noop!( + ParaRegistrar::unpause_container_chain(RuntimeOrigin::root(), 42.into()), + Error::::ParaIdNotPaused + ); + }); +} + +#[test] +fn genesis_loads_para_ids() { + new_test_ext_with_genesis(vec![ + (1.into(), empty_genesis_data()), + (2.into(), empty_genesis_data()), + (3.into(), empty_genesis_data()), + (4.into(), empty_genesis_data()), + ]) + .execute_with(|| { + run_to_block(1); + assert_eq!( + ParaRegistrar::registered_para_ids(), + vec![1.into(), 2.into(), 3.into(), 4.into()] + ); + }); +} + +#[test] +fn genesis_sorts_para_ids() { + new_test_ext_with_genesis(vec![ + (4.into(), empty_genesis_data()), + (2.into(), empty_genesis_data()), + (3.into(), empty_genesis_data()), + (1.into(), empty_genesis_data()), + ]) + .execute_with(|| { + run_to_block(1); + assert_eq!( + ParaRegistrar::registered_para_ids(), + vec![1.into(), 2.into(), 3.into(), 4.into()] + ); + }); +} + +#[test] +#[should_panic = "Duplicate para_id: 2"] +fn genesis_error_on_duplicate() { + new_test_ext_with_genesis(vec![ + (2.into(), empty_genesis_data()), + (3.into(), empty_genesis_data()), + (4.into(), empty_genesis_data()), + (2.into(), empty_genesis_data()), + ]) + .execute_with(|| { + run_to_block(1); + }); +} + +#[test] +#[should_panic = "genesis data for para_id 2 is too large: 5000024 bytes"] +fn genesis_error_genesis_data_size_too_big() { + let genesis_data = ContainerChainGenesisData { + storage: vec![(vec![], vec![0; 5_000_000]).into()], + name: Default::default(), + id: Default::default(), + fork_id: Default::default(), + extensions: Default::default(), + properties: Default::default(), + }; + new_test_ext_with_genesis(vec![(2.into(), genesis_data)]).execute_with(|| { + run_to_block(1); + }); +} + +#[test] +fn register_without_mark_valid_for_collating() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdRegistered { para_id: 42.into() }.into()); + assert_eq!(ParaRegistrar::pending_registered_para_ids(), vec![]); + + // Assert after two sessions registered para ids are still empty + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::pending_registered_para_ids(), vec![]); + }); +} + +#[test] +fn mark_valid_for_collating_twice() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_noop!( + ParaRegistrar::mark_valid_for_collating(RuntimeOrigin::root(), 42.into(),), + Error::::ParaIdNotInPendingVerification + ); + }); +} + +#[test] +fn mark_valid_for_collating_invalid_para_id() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_noop!( + ParaRegistrar::mark_valid_for_collating(RuntimeOrigin::root(), 1.into(),), + Error::::ParaIdNotInPendingVerification + ); + }); +} + +#[test] +fn mark_valid_for_collating_already_valid_para_id() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdRegistered { para_id: 42.into() }.into()); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![42.into()]); + assert_eq!(ParaRegistrar::pending_registered_para_ids(), vec![]); + assert_noop!( + ParaRegistrar::mark_valid_for_collating(RuntimeOrigin::root(), 42.into(),), + Error::::ParaIdNotInPendingVerification + ); + }); +} + +#[test] +fn mark_valid_for_collating_calls_registered_hook() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_eq!(Mock::get().called_hooks, vec![]); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!( + Mock::get().called_hooks, + vec![HookCall::MarkedValid(42.into())] + ); + }); +} + +#[test] +fn deregister_returns_bond_immediately_if_not_marked_as_valid() { + new_test_ext().execute_with(|| { + run_to_block(1); + let bond = DepositAmount::get(); + let balance_before = Balances::free_balance(ALICE); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_eq!(Balances::free_balance(ALICE), balance_before - bond); + + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into(),)); + + // Bond is returned immediately + assert_eq!(Balances::free_balance(ALICE), balance_before); + }); +} + +#[test] +fn deregister_returns_bond_after_2_sessions_if_marked_as_valid() { + new_test_ext().execute_with(|| { + run_to_block(1); + let bond = DepositAmount::get(); + let balance_before = Balances::free_balance(ALICE); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!(Balances::free_balance(ALICE), balance_before - bond); + + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into(),)); + + // Bond is returned after 2 sessions + assert_eq!(Balances::free_balance(ALICE), balance_before - bond); + run_to_session(2); + assert_eq!(Balances::free_balance(ALICE), balance_before); + }); +} + +#[test] +fn can_deregister_before_valid_for_collating() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into(),)); + System::assert_has_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + }); +} + +#[test] +fn can_deregister_paused_para_id_after_0_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + // Pause and deregister in the same block + assert_ok!(ParaRegistrar::pause_container_chain( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(1); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn can_deregister_paused_para_id_after_1_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + // Pause, wait 1 session, and deregister + assert_ok!(ParaRegistrar::pause_container_chain( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + + run_to_session(1); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::paused(), vec![]); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::paused(), vec![42.into()]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(3); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::paused(), vec![]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn can_deregister_paused_para_id_after_2_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_ok!(ParaRegistrar::pause_container_chain( + RuntimeOrigin::root(), + 42.into(), + )); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + + run_to_session(2); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + // Pause, wait 2 sessions, and deregister + assert_eq!(ParaRegistrar::paused(), vec![42.into()]); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + // Assert that the correct event was deposited + System::assert_last_event(Event::ParaIdDeregistered { para_id: 42.into() }.into()); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::paused(), vec![42.into()]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(3); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::paused(), vec![42.into()]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + run_to_session(4); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!(ParaRegistrar::paused(), vec![]); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn cannot_register_same_para_id_while_deregister_is_pending() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into(),)); + assert_noop!( + ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data(), + ), + Error::::ParaIdAlreadyRegistered, + ); + run_to_session(1); + assert_noop!( + ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data(), + ), + Error::::ParaIdAlreadyRegistered, + ); + run_to_session(2); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + }); +} + +#[test] +fn register_deregister_register_in_same_block() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into(),)); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + None + ); + let new_genesis_data = ContainerChainGenesisData { + storage: vec![(b"key".to_vec(), b"value".to_vec()).into()], + name: Default::default(), + id: Default::default(), + fork_id: Default::default(), + extensions: Default::default(), + properties: Default::default(), + }; + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + new_genesis_data.clone(), + )); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + Some(&new_genesis_data) + ); + run_to_session(2); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + Some(&new_genesis_data) + ); + }); +} + +#[test] +fn deregister_2_container_chains_in_same_block() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 43.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 43.into(), + )); + + run_to_session(2); + assert_eq!( + ParaRegistrar::registered_para_ids(), + vec![42.into(), 43.into()] + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(43)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into(),)); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 43.into(),)); + assert_eq!( + Mock::get().called_hooks, + vec![ + HookCall::MarkedValid(42.into()), + HookCall::MarkedValid(43.into()), + ] + ); + + run_to_session(4); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + None + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(43)).as_ref(), + None + ); + assert_eq!( + Mock::get().called_hooks, + vec![ + HookCall::MarkedValid(42.into()), + HookCall::MarkedValid(43.into()), + HookCall::Deregistered(42.into()), + HookCall::Deregistered(43.into()), + ] + ); + }); +} + +#[test] +fn deregister_2_container_chains_in_consecutive_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 43.into(), + empty_genesis_data() + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 43.into(), + )); + + run_to_session(2); + assert_eq!( + ParaRegistrar::registered_para_ids(), + vec![42.into(), 43.into()] + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(43)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into(),)); + + run_to_session(3); + assert_eq!( + ParaRegistrar::registered_para_ids(), + vec![42.into(), 43.into()] + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(43)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 43.into(),)); + assert_eq!( + Mock::get().called_hooks, + vec![ + HookCall::MarkedValid(42.into()), + HookCall::MarkedValid(43.into()), + ] + ); + + run_to_session(4); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![43.into()]); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + None + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(43)).as_ref(), + Some(&empty_genesis_data()) + ); + assert_eq!( + Mock::get().called_hooks, + vec![ + HookCall::MarkedValid(42.into()), + HookCall::MarkedValid(43.into()), + HookCall::Deregistered(42.into()), + ] + ); + + run_to_session(5); + assert_eq!(ParaRegistrar::registered_para_ids(), vec![]); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(42)).as_ref(), + None + ); + assert_eq!( + ParaRegistrar::para_genesis_data(ParaId::from(43)).as_ref(), + None + ); + assert_eq!( + Mock::get().called_hooks, + vec![ + HookCall::MarkedValid(42.into()), + HookCall::MarkedValid(43.into()), + HookCall::Deregistered(42.into()), + HookCall::Deregistered(43.into()), + ] + ); + }); +} + +#[test] +fn deposit_removed_on_deregister_if_not_marked_as_valid() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_some()); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + // Deregister a para id that was not marked as valid_for_collating, deposit and genesis data are + // removed immediately because no collators are assigned to this chain. + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_none()); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn deposit_removed_after_2_sessions_if_marked_as_valid() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_some()); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + + // Deregister a para id that has been marked as valid_for_collating, deposit and genesis data + // will be stored until all collators are unassigned, after 2 sessions. + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_some()); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + + // Deposit removed after 2 sessions + run_to_session(2); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_none()); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn parathread_change_params_after_two_sessions() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register_parathread( + RuntimeOrigin::signed(ALICE), + 42.into(), + SlotFrequency { min: 1, max: 1 }, + empty_genesis_data() + )); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_some()); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_ok!(ParaRegistrar::set_parathread_params( + RuntimeOrigin::root(), + ParaId::from(42), + SlotFrequency { min: 2, max: 2 } + )); + // Params are not updated immediately + assert_eq!( + ParaRegistrar::parathread_params(ParaId::from(42)).map(|x| x.slot_frequency), + Some(SlotFrequency { min: 1, max: 1 }) + ); + + // Params are updated after 2 sessions + run_to_session(2); + assert_eq!( + ParaRegistrar::parathread_params(ParaId::from(42)).map(|x| x.slot_frequency), + Some(SlotFrequency { min: 2, max: 2 }) + ); + }); +} + +#[test] +fn parathread_params_cannot_be_set_for_parachains() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register( + RuntimeOrigin::signed(ALICE), + 42.into(), + empty_genesis_data() + )); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_some()); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_noop!( + ParaRegistrar::set_parathread_params( + RuntimeOrigin::root(), + ParaId::from(42), + SlotFrequency { min: 2, max: 2 } + ), + Error::::NotAParathread + ); + }); +} + +#[test] +fn parathread_register_change_params_deregister() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register_parathread( + RuntimeOrigin::signed(ALICE), + 42.into(), + SlotFrequency { min: 1, max: 1 }, + empty_genesis_data() + )); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_some()); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + assert_ok!(ParaRegistrar::set_parathread_params( + RuntimeOrigin::root(), + ParaId::from(42), + SlotFrequency { min: 2, max: 2 } + )); + + // Deregister parathread while parathread params are pending + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + assert_eq!( + ParaRegistrar::parathread_params(ParaId::from(42)).map(|x| x.slot_frequency), + Some(SlotFrequency { min: 1, max: 1 }) + ); + + // Params removed after 2 sessions + run_to_session(2); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + assert!(ParaRegistrar::parathread_params(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn parathread_register_deregister_change_params() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(ParaRegistrar::register_parathread( + RuntimeOrigin::signed(ALICE), + 42.into(), + SlotFrequency { min: 1, max: 1 }, + empty_genesis_data() + )); + assert!(ParaRegistrar::registrar_deposit(ParaId::from(42)).is_some()); + assert_ok!(ParaRegistrar::mark_valid_for_collating( + RuntimeOrigin::root(), + 42.into(), + )); + + // Deregister parathread while parathread params are pending + assert_ok!(ParaRegistrar::deregister(RuntimeOrigin::root(), 42.into())); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_some()); + assert!(ParaRegistrar::parathread_params(ParaId::from(42)).is_some()); + + run_to_session(1); + assert_ok!(ParaRegistrar::set_parathread_params( + RuntimeOrigin::root(), + ParaId::from(42), + SlotFrequency { min: 2, max: 2 } + )); + + // Params removed after 2 sessions + run_to_session(2); + assert!(ParaRegistrar::para_genesis_data(ParaId::from(42)).is_none()); + assert!(ParaRegistrar::parathread_params(ParaId::from(42)).is_none()); + + // Params not updated after 3 sessions + run_to_session(3); + assert!(ParaRegistrar::parathread_params(ParaId::from(42)).is_none()); + }); +} + +#[test] +fn weights_assigned_to_extrinsics_are_correct() { + new_test_ext().execute_with(|| { + assert_eq!( + crate::Call::::register { + para_id: 42.into(), + genesis_data: empty_genesis_data() + } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::register( + empty_genesis_data().encoded_size() as u32, + ::MaxLengthParaIds::get(), + 0 + ) + ); + + assert_eq!( + crate::Call::::deregister { para_id: 42.into() } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::deregister_immediate( + ::MaxGenesisDataSize::get(), + ::MaxLengthParaIds::get() + ) + .max(<() as crate::weights::WeightInfo>::deregister_scheduled( + ::MaxGenesisDataSize::get(), + ::MaxLengthParaIds::get() + )) + ); + + assert_eq!( + crate::Call::::mark_valid_for_collating { para_id: 42.into() } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::mark_valid_for_collating( + ::MaxLengthParaIds::get() + ) + ); + + assert_eq!( + crate::Call::::pause_container_chain { para_id: 42.into() } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::pause_container_chain( + ::MaxLengthParaIds::get() + ) + ); + + assert_eq!( + crate::Call::::unpause_container_chain { para_id: 42.into() } + .get_dispatch_info() + .weight, + <() as crate::weights::WeightInfo>::unpause_container_chain( + ::MaxLengthParaIds::get() + ) + ); + }); +} diff --git a/pallets/registrar/src/weights.rs b/pallets/registrar/src/weights.rs new file mode 100644 index 0000000..6d0ba39 --- /dev/null +++ b/pallets/registrar/src/weights.rs @@ -0,0 +1,467 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_registrar +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-01-30, STEPS: `16`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `tomasz-XPS-15-9520`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_registrar +// --extrinsic +// * +// --chain=dev +// --steps +// 16 +// --repeat +// 1 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// tmp/pallet_registrar.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_registrar. +pub trait WeightInfo { + fn register(x: u32, y: u32, z: u32, ) -> Weight; + fn deregister_immediate(x: u32, y: u32, ) -> Weight; + fn deregister_scheduled(x: u32, y: u32, ) -> Weight; + fn mark_valid_for_collating(y: u32, ) -> Weight; + fn pause_container_chain(y: u32, ) -> Weight; + fn unpause_container_chain(y: u32, ) -> Weight; + fn register_parathread(x: u32, y: u32, z: u32, ) -> Weight; + fn set_parathread_params(y: u32, ) -> Weight; +} + +/// Weights for pallet_registrar using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Registrar::ParaGenesisData` (r:1 w:1) + /// Proof: `Registrar::ParaGenesisData` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegistrarDeposit` (r:0 w:1) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + /// The range of component `z` is `[1, 10]`. + fn register(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `350 + y * (13 ±0)` + // Estimated: `3809 + y * (13 ±0) + z * (3 ±2)` + // Minimum execution time: 38_408_000 picoseconds. + Weight::from_parts(38_408_000, 3809) + // Standard Error: 53 + .saturating_add(Weight::from_parts(751, 0).saturating_mul(x.into())) + // Standard Error: 16_331_035 + .saturating_add(Weight::from_parts(115_744_655, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 13).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(z.into())) + } + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:1) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ServicesPayment::BlockProductionCredits` (r:0 w:1) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `Registrar::ParaGenesisData` (r:0 w:1) + /// Proof: `Registrar::ParaGenesisData` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::ParathreadParams` (r:0 w:1) + /// Proof: `Registrar::ParathreadParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:0 w:1) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AuthorNoting::LatestAuthor` (r:0 w:1) + /// Proof: `AuthorNoting::LatestAuthor` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + fn deregister_immediate(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `250 + y * (17 ±0)` + // Estimated: `3785 + y * (15 ±0)` + // Minimum execution time: 48_129_000 picoseconds. + Weight::from_parts(52_650_930, 3785) + // Standard Error: 1 + .saturating_add(Weight::from_parts(2, 0).saturating_mul(x.into())) + // Standard Error: 87_727 + .saturating_add(Weight::from_parts(372_523, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_parts(0, 15).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingVerification` (r:1 w:0) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingPaused` (r:1 w:0) + /// Proof: `Registrar::PendingPaused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegisteredParaIds` (r:1 w:0) + /// Proof: `Registrar::RegisteredParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paused` (r:1 w:0) + /// Proof: `Registrar::Paused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingToRemove` (r:1 w:1) + /// Proof: `Registrar::PendingToRemove` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + fn deregister_scheduled(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `396 + y * (4 ±0)` + // Estimated: `1879 + y * (4 ±0)` + // Minimum execution time: 23_181_000 picoseconds. + Weight::from_parts(24_298_819, 1879) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1, 0).saturating_mul(x.into())) + // Standard Error: 91_621 + .saturating_add(Weight::from_parts(304_778, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegisteredParaIds` (r:1 w:0) + /// Proof: `Registrar::RegisteredParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:1 w:0) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::GivenFreeCredits` (r:1 w:1) + /// Proof: `ServicesPayment::GivenFreeCredits` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `ServicesPayment::BlockProductionCredits` (r:1 w:1) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// The range of component `y` is `[1, 50]`. + fn mark_valid_for_collating(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1111 + y * (36 ±0)` + // Estimated: `4514 + y * (36 ±2)` + // Minimum execution time: 49_292_000 picoseconds. + Weight::from_parts(58_444_147, 4514) + // Standard Error: 202_350 + .saturating_add(Weight::from_parts(578_764, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingPaused` (r:1 w:1) + /// Proof: `Registrar::PendingPaused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[1, 50]`. + fn pause_container_chain(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `428 + y * (8 ±0)` + // Estimated: `1912 + y * (8 ±0)` + // Minimum execution time: 30_666_000 picoseconds. + Weight::from_parts(35_290_910, 1912) + // Standard Error: 50_241 + .saturating_add(Weight::from_parts(104_888, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingPaused` (r:1 w:1) + /// Proof: `Registrar::PendingPaused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[1, 50]`. + fn unpause_container_chain(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `428 + y * (8 ±0)` + // Estimated: `1912 + y * (8 ±0)` + // Minimum execution time: 25_774_000 picoseconds. + Weight::from_parts(29_010_669, 1912) + // Standard Error: 39_979 + .saturating_add(Weight::from_parts(267_751, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(y.into())) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Registrar::ParaGenesisData` (r:1 w:1) + /// Proof: `Registrar::ParaGenesisData` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::ParathreadParams` (r:0 w:1) + /// Proof: `Registrar::ParathreadParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegistrarDeposit` (r:0 w:1) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + /// The range of component `z` is `[1, 10]`. + fn register_parathread(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `384 + y * (13 ±0)` + // Estimated: `3833 + y * (13 ±0) + z * (3 ±2)` + // Minimum execution time: 42_264_000 picoseconds. + Weight::from_parts(42_264_000, 3833) + // Standard Error: 51 + .saturating_add(Weight::from_parts(722, 0).saturating_mul(x.into())) + // Standard Error: 15_908_841 + .saturating_add(Weight::from_parts(112_465_553, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 13).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(z.into())) + } + /// Storage: `Registrar::ParathreadParams` (r:1 w:0) + /// Proof: `Registrar::ParathreadParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParathreadParams` (r:1 w:1) + /// Proof: `Registrar::PendingParathreadParams` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[1, 50]`. + fn set_parathread_params(_y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `483` + // Estimated: `3948` + // Minimum execution time: 19_970_000 picoseconds. + Weight::from_parts(23_219_566, 3948) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Registrar::ParaGenesisData` (r:1 w:1) + /// Proof: `Registrar::ParaGenesisData` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegistrarDeposit` (r:0 w:1) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + /// The range of component `z` is `[1, 10]`. + fn register(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `350 + y * (13 ±0)` + // Estimated: `3809 + y * (13 ±0) + z * (3 ±2)` + // Minimum execution time: 38_408_000 picoseconds. + Weight::from_parts(38_408_000, 3809) + // Standard Error: 53 + .saturating_add(Weight::from_parts(751, 0).saturating_mul(x.into())) + // Standard Error: 16_331_035 + .saturating_add(Weight::from_parts(115_744_655, 0).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 13).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(z.into())) + } + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:1) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ServicesPayment::BlockProductionCredits` (r:0 w:1) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `Registrar::ParaGenesisData` (r:0 w:1) + /// Proof: `Registrar::ParaGenesisData` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::ParathreadParams` (r:0 w:1) + /// Proof: `Registrar::ParathreadParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:0 w:1) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AuthorNoting::LatestAuthor` (r:0 w:1) + /// Proof: `AuthorNoting::LatestAuthor` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + fn deregister_immediate(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `250 + y * (17 ±0)` + // Estimated: `3785 + y * (15 ±0)` + // Minimum execution time: 48_129_000 picoseconds. + Weight::from_parts(52_650_930, 3785) + // Standard Error: 1 + .saturating_add(Weight::from_parts(2, 0).saturating_mul(x.into())) + // Standard Error: 87_727 + .saturating_add(Weight::from_parts(372_523, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_parts(0, 15).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingVerification` (r:1 w:0) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingPaused` (r:1 w:0) + /// Proof: `Registrar::PendingPaused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegisteredParaIds` (r:1 w:0) + /// Proof: `Registrar::RegisteredParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paused` (r:1 w:0) + /// Proof: `Registrar::Paused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingToRemove` (r:1 w:1) + /// Proof: `Registrar::PendingToRemove` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + fn deregister_scheduled(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `396 + y * (4 ±0)` + // Estimated: `1879 + y * (4 ±0)` + // Minimum execution time: 23_181_000 picoseconds. + Weight::from_parts(24_298_819, 1879) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1, 0).saturating_mul(x.into())) + // Standard Error: 91_621 + .saturating_add(Weight::from_parts(304_778, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegisteredParaIds` (r:1 w:0) + /// Proof: `Registrar::RegisteredParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `DataPreservers::BootNodes` (r:1 w:0) + /// Proof: `DataPreservers::BootNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::GivenFreeCredits` (r:1 w:1) + /// Proof: `ServicesPayment::GivenFreeCredits` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `ServicesPayment::BlockProductionCredits` (r:1 w:1) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// The range of component `y` is `[1, 50]`. + fn mark_valid_for_collating(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1111 + y * (36 ±0)` + // Estimated: `4514 + y * (36 ±2)` + // Minimum execution time: 49_292_000 picoseconds. + Weight::from_parts(58_444_147, 4514) + // Standard Error: 202_350 + .saturating_add(Weight::from_parts(578_764, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingPaused` (r:1 w:1) + /// Proof: `Registrar::PendingPaused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[1, 50]`. + fn pause_container_chain(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `428 + y * (8 ±0)` + // Estimated: `1912 + y * (8 ±0)` + // Minimum execution time: 30_666_000 picoseconds. + Weight::from_parts(35_290_910, 1912) + // Standard Error: 50_241 + .saturating_add(Weight::from_parts(104_888, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(y.into())) + } + /// Storage: `Registrar::PendingParaIds` (r:1 w:1) + /// Proof: `Registrar::PendingParaIds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingPaused` (r:1 w:1) + /// Proof: `Registrar::PendingPaused` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[1, 50]`. + fn unpause_container_chain(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `428 + y * (8 ±0)` + // Estimated: `1912 + y * (8 ±0)` + // Minimum execution time: 25_774_000 picoseconds. + Weight::from_parts(29_010_669, 1912) + // Standard Error: 39_979 + .saturating_add(Weight::from_parts(267_751, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(y.into())) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Registrar::ParaGenesisData` (r:1 w:1) + /// Proof: `Registrar::ParaGenesisData` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingVerification` (r:1 w:1) + /// Proof: `Registrar::PendingVerification` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::ParathreadParams` (r:0 w:1) + /// Proof: `Registrar::ParathreadParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::RegistrarDeposit` (r:0 w:1) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 3000000]`. + /// The range of component `y` is `[1, 50]`. + /// The range of component `z` is `[1, 10]`. + fn register_parathread(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `384 + y * (13 ±0)` + // Estimated: `3833 + y * (13 ±0) + z * (3 ±2)` + // Minimum execution time: 42_264_000 picoseconds. + Weight::from_parts(42_264_000, 3833) + // Standard Error: 51 + .saturating_add(Weight::from_parts(722, 0).saturating_mul(x.into())) + // Standard Error: 15_908_841 + .saturating_add(Weight::from_parts(112_465_553, 0).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 13).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 3).saturating_mul(z.into())) + } + /// Storage: `Registrar::ParathreadParams` (r:1 w:0) + /// Proof: `Registrar::ParathreadParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingParathreadParams` (r:1 w:1) + /// Proof: `Registrar::PendingParathreadParams` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[1, 50]`. + fn set_parathread_params(_y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `483` + // Estimated: `3948` + // Minimum execution time: 19_970_000 picoseconds. + Weight::from_parts(23_219_566, 3948) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/schelling-game-shared/Cargo.toml b/pallets/schelling-game-shared/Cargo.toml deleted file mode 100644 index 9d47931..0000000 --- a/pallets/schelling-game-shared/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "schelling-game-shared" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -num-integer = {default-features = false, version= "0.1.44"} -sortition-sum-game = {default-features = false, path="../sortition-sum-game"} -sortition-sum-game-link = {default-features = false, path="../../traits/sortition-sum-game-link"} -schelling-game-shared-link = {default-features=false, path="../../traits/schelling-game-shared-link"} - - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support-test = { version = "3.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "pallet-balances/std", - "sp-io/std", - "num-integer/std", - "sortition-sum-game/std", - "sortition-sum-game-link/std", - "schelling-game-shared-link/std", - - -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/schelling-game-shared/src/benchmarking.rs b/pallets/schelling-game-shared/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/schelling-game-shared/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/schelling-game-shared/src/extras.rs b/pallets/schelling-game-shared/src/extras.rs deleted file mode 100644 index d4b7466..0000000 --- a/pallets/schelling-game-shared/src/extras.rs +++ /dev/null @@ -1,855 +0,0 @@ -use crate::*; - -impl Pallet { - pub(super) fn create_phase_with_all_data( - evidence_length: u64, - end_of_staking_time: u64, - staking_length: u64, - drawing_length: u64, - commit_length: u64, - vote_length: u64, - appeal_length: u64, - max_draws: u64, - min_number_juror_staked: u64, - min_juror_stake: u64, - juror_incentives: (u64, u64), - ) -> PhaseDataOf { - PhaseData::create_phase_with_all_data( - evidence_length, - end_of_staking_time, - staking_length, - drawing_length, - commit_length, - vote_length, - appeal_length, - max_draws, - min_number_juror_staked, - min_juror_stake, - juror_incentives, - ) - } - pub(super) fn create_phase_data( - block_length: u64, - max_draws: u64, - min_number_juror_staked: u64, - min_juror_stake: u64, - juror_incentives: (u64, u64), - ) -> PhaseDataOf { - PhaseData::create_with_data( - block_length, - max_draws, - min_number_juror_staked, - min_juror_stake, - juror_incentives, - ) - } - /// Set to evidence period, when some one stakes for validation - pub(super) fn set_to_evidence_period( - key: SumTreeNameType, - now: BlockNumberOf, - ) -> DispatchResult { - match >::get(&key) { - Some(_period) => Err(Error::::PeriodExists)?, - None => { - let period = Period::Evidence; - >::insert(&key, period); - >::insert(&key, now); - }, - } - Ok(()) - } - - /// Check `Period` is `Evidence`, and change it to `Staking` - /// It is called with function that submits challenge stake after `end_block` of evidence period - /// Checks evidence period is over - #[doc=include_str!("docimage/set_to_staking_period_1.svg")] - /// ```ignore - /// if time >= block_time.min_short_block_length { - /// // change `Period` to `Staking` - /// } - /// ``` - pub(super) fn set_to_staking_period( - key: SumTreeNameType, - phase_data: PhaseDataOf, - now: BlockNumberOf, - ) -> DispatchResult { - if let Some(Period::Evidence) = >::get(&key) { - let evidence_stake_block_number = >::get(&key); - let time = now.checked_sub(&evidence_stake_block_number).expect("Overflow"); - let evidence_length = phase_data.evidence_length; - let end_length_for_staking = phase_data.end_of_staking_time; - let total_length = - evidence_length.checked_add(&end_length_for_staking).expect("overflow"); - if time >= evidence_length && time < total_length { - let new_period = Period::Staking; - >::insert(&key, new_period); - >::insert(&key, now); - } else if time >= total_length { - Err(Error::::TimeForStakingOver)? - } else { - Err(Error::::EvidencePeriodNotOver)? - } - } else { - Err(Error::::PeriodIsNotEvidence)? - } - - Ok(()) - } - - /// Check time for staking over - pub(super) fn ensure_time_for_staking_over( - key: SumTreeNameType, - phase_data: PhaseDataOf, - now: BlockNumberOf, - ) -> DispatchResult { - let evidence_stake_block_number = >::get(&key); - let time = now.checked_sub(&evidence_stake_block_number).expect("Overflow"); - let evidence_length = phase_data.evidence_length; - let end_length_for_staking = phase_data.end_of_staking_time; - let total_length = evidence_length.checked_add(&end_length_for_staking).expect("overflow"); - ensure!(time >= total_length, Error::::TimeForStakingNotOver); - Ok(()) - } - - /// Set staking period when evidence period is not required - pub(super) fn set_to_staking_period_pe( - key: SumTreeNameType, - now: BlockNumberOf, - ) -> DispatchResult { - if let None = >::get(&key) { - let new_period = Period::Staking; - >::insert(&key, new_period); - >::insert(&key, now); - } else { - Err(Error::::PeriodIsNotNone)? - } - - Ok(()) - } - - pub(super) fn create_tree_link_helper(key: SumTreeNameType, k: u64) -> DispatchResult { - T::SortitionSumGameSource::create_tree_link(key.clone(), k)?; - Ok(()) - } - - /// Change the `Period` - /// - /// `Period::Staking` to `Period::Drawing` - #[doc=include_str!("docimage/change_period_link_1.svg")] - /// ```ignore - /// if now >= min_long_block_length + staking_start_time { - /// // Change `Period::Staking` to `Period::Drawing` - /// } - /// ``` - /// - /// `Period::Drawing` to `Period::Commit` - /// When maximum juror are drawn - /// - /// `Period::Commit` to `Period::Vote` - /// ```ignore - /// if now >= min_long_block_length + commit_start_time { - /// // Change `Period::Commit` to `Period::Vote` - /// } - /// ``` - /// - /// `Period::Vote` to `Period::Execution` - /// ```ignore - /// if now >= min_long_block_length + vote_start_time { - /// // Change `Period::Vote` to `Period::Execution` - /// } - /// ``` - pub(super) fn change_period( - key: SumTreeNameType, - phase_data: PhaseDataOf, - now: BlockNumberOf, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - match period { - Period::Evidence => todo!(), - Period::Staking => { - // Also check has min number of jurors has staked - let staking_start_time = >::get(&key); - let staking_length = phase_data.staking_length; - if now >= staking_length + staking_start_time { - let new_period = Period::Drawing; - >::insert(&key, new_period); - } else { - Err(Error::::StakingPeriodNotOver)? - } - }, - Period::Drawing => { - // Also give time - let max_draws = phase_data.max_draws; - let draws_in_round = >::get(&key); - if draws_in_round >= max_draws { - >::insert(&key, now); - let new_period = Period::Commit; - >::insert(&key, new_period); - } else { - Err(Error::::MaxJurorNotDrawn)? - } - }, - Period::Commit => { - let commit_start_time = >::get(&key); - let commit_length = phase_data.commit_length; - if now >= commit_length + commit_start_time { - >::insert(&key, now); - let new_period = Period::Vote; - >::insert(&key, new_period); - } else { - Err(Error::::CommitPeriodNotOver)? - } - }, - Period::Vote => { - let vote_start_time = >::get(&key); - let vote_length = phase_data.vote_length; - if now >= vote_length + vote_start_time { - let new_period = Period::Execution; - >::insert(&key, new_period); - } else { - Err(Error::::VotePeriodNotOver)? - } - }, - Period::Appeal => todo!(), - Period::Execution => todo!(), - } - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - Ok(()) - } - - pub(super) fn apply_jurors_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - who: AccountIdOf, - stake: BalanceOf, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Staking, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - let min_stake = phase_data.min_juror_stake; - - ensure!(stake >= min_stake, Error::::JurorStakeLessThanMin); - - // let imb = T::Currency::withdraw( - // &who, - // stake, - // WithdrawReasons::TRANSFER, - // ExistenceRequirement::AllowDeath, - // )?; - - // T::Currency::resolve_creating(&Self::juror_stake_account(), imb); - - let imbalance = T::Currency::slash(&who, stake).0; - T::Slash::on_unbalanced(imbalance); - - // let stake_of = Self::stake_of(key.clone(), profile_citizenid)?; - - let stake_u64 = Self::balance_to_u64_saturated(stake); - - let stake_of = T::SortitionSumGameSource::stake_of_link(key.clone(), who.clone())?; - - match stake_of { - Some(_stake) => Err(Error::::AlreadyStaked)?, - None => { - let result = T::SortitionSumGameSource::set_link(key, stake_u64, who); - result - }, - } - } - - // Improvements: Set stake to zero after a juror is drawn, so that they are not drawn again. Store the stake in storage map in DrawnJurors, and use it in get_incentives_helper - pub(super) fn draw_jurors_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - iterations: u64, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Drawing, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - let max_draws = phase_data.max_draws; - let draws_in_round = >::get(&key); - ensure!(draws_in_round < max_draws.into(), Error::::MaxDrawExceeded); - let mut end_index = draws_in_round + iterations; - if draws_in_round + iterations >= max_draws { - end_index = max_draws; - } - let mut draw_increment = draws_in_round.clone(); - - for _ in draws_in_round..end_index { - let nonce = Self::get_and_increment_nonce(); - let random_seed = T::RandomnessSource::random(&nonce).encode(); - let random_number = u64::decode(&mut random_seed.as_ref()) - .expect("secure hashes should always be bigger than u64; qed"); - // let mut rng = rand::thread_rng(); - // let random_number: u64 = rng.gen(); - // log::info!("Random number: {:?}", random_number); - let accountid = T::SortitionSumGameSource::draw_link(key.clone(), random_number)?; - let stake = T::SortitionSumGameSource::stake_of_link(key.clone(), accountid.clone())?; - - let mut drawn_juror = >::get(&key); - match drawn_juror.binary_search_by(|(c, _)| c.cmp(&accountid)) { - Ok(_) => {}, - Err(index) => { - drawn_juror.insert(index, (accountid.clone(), stake.unwrap())); - >::insert(&key, drawn_juror); - draw_increment = draw_increment + 1; - // println!("draw_increment, {:?}", draw_increment); - T::SortitionSumGameSource::set_link(key.clone(), 0, accountid)?; - }, - } - >::insert(&key, draw_increment); - } - Ok(()) - } - - // When DrawnJurors contains stake, use drawn_juror.binary_search_by(|(c, _)| c.cmp(&who)); - pub(super) fn unstaking_helper(key: SumTreeNameType, who: AccountIdOf) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!( - period == Period::Commit - || period == Period::Vote - || period == Period::Execution, - Error::::PeriodDontMatch - ); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - - let drawn_juror = >::get(&key); - match drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) { - Ok(_) => Err(Error::::SelectedAsJuror)?, - Err(_) => {}, - } - - let stake_of = T::SortitionSumGameSource::stake_of_link(key.clone(), who.clone())?; - - match stake_of { - Some(stake) => { - let balance = Self::u64_to_balance_saturated(stake); - let mut unstaked_jurors = >::get(&key); - match unstaked_jurors.binary_search(&who) { - Ok(_) => Err(Error::::AlreadyUnstaked)?, - Err(index) => { - unstaked_jurors.insert(index, who.clone()); - >::insert(&key, unstaked_jurors); - // let _ = T::Currency::resolve_into_existing( - // &who, - // T::Currency::withdraw( - // &Self::juror_stake_account(), - // balance, - // WithdrawReasons::TRANSFER, - // ExistenceRequirement::AllowDeath, - // )?, - // ); - let r = T::Currency::deposit_into_existing(&who, balance).ok().unwrap(); - T::Reward::on_unbalanced(r); - }, - } - }, - None => Err(Error::::StakeDoesNotExists)?, - } - - // println!("stakeof {:?}", stake_of); - - Ok(()) - } - - pub(super) fn commit_vote_helper( - key: SumTreeNameType, - who: AccountIdOf, - vote_commit: [u8; 32], - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Commit, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - let drawn_jurors = >::get(&key); - match drawn_jurors.binary_search_by(|(c, _)| c.cmp(&who.clone())) { - Ok(_) => { - let vote_commit_struct = CommitVote { - commit: vote_commit, - votestatus: VoteStatus::Commited, - revealed_vote: None, - }; - >::insert(&key, &who, vote_commit_struct); - }, - Err(_) => Err(Error::::JurorDoesNotExists)?, - } - Ok(()) - } - - pub(super) fn reveal_vote_two_choice_helper( - key: SumTreeNameType, - who: AccountIdOf, - choice: u128, - salt: Vec, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Vote, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - let who_commit_vote = >::get(&key, &who); - match who_commit_vote { - Some(mut commit_struct) => { - ensure!( - commit_struct.votestatus == VoteStatus::Commited, - Error::::VoteStatusNotCommited - ); - let mut vote = format!("{}", choice).as_bytes().to_vec(); - // let mut vote = choice.clone(); - let mut salt_a = salt.clone(); - vote.append(&mut salt_a); - let vote_bytes: &[u8] = &vote; - let hash = sp_io::hashing::keccak_256(vote_bytes); - let commit: &[u8] = &commit_struct.commit; - if hash == commit { - let mut decision_tuple = >::get(&key); - if choice == 1 { - decision_tuple.1 = decision_tuple.1 + 1; - >::insert(&key, decision_tuple); - commit_struct.revealed_vote = Some(RevealedVote::Yes); - } else if choice == 0 { - decision_tuple.0 = decision_tuple.0 + 1; - >::insert(&key, decision_tuple); - commit_struct.revealed_vote = Some(RevealedVote::No); - } else { - Err(Error::::NotValidChoice)? - } - commit_struct.votestatus = VoteStatus::Revealed; - >::insert(&key, &who, commit_struct); - } else { - Err(Error::::CommitDoesNotMatch)? - } - }, - None => Err(Error::::CommitDoesNotExists)?, - } - - Ok(()) - } - - /// Distribute incentives in a single go. - pub(super) fn get_all_incentives_two_choice_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Execution, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - - let drawn_jurors = >::get(&key); - let reveal_votes_iterator = >::iter_prefix(&key); - - let mut reveal_votes = reveal_votes_iterator - .map(|(account_id, commit_vote)| (account_id, commit_vote.revealed_vote)) - .collect::>(); - reveal_votes.sort_by(|a, b| a.0.cmp(&b.0)); - let decision_count = >::get(&key); - let incentives = phase_data.juror_incentives; - let (winning_decision, winning_incentives) = - Self::get_winning_incentives(decision_count, incentives); - for juror in drawn_jurors { - match reveal_votes.binary_search_by(|(c, _)| c.cmp(&juror.0)) { - Ok(index) => { - let account_n_vote = reveal_votes[index].clone(); - if let Some(vote) = account_n_vote.1 { - match winning_decision { - WinningDecision::WinnerYes => match vote { - RevealedVote::Yes => { - let result = Self::winner_getting_incentives2( - juror.0.clone(), - winning_incentives, - juror.1, - )?; - result - }, - RevealedVote::No => { - let result = - Self::looser_getting_incentives2(juror.0.clone(), juror.1)?; - result - }, - }, - WinningDecision::WinnerNo => match vote { - RevealedVote::Yes => { - let result = - Self::looser_getting_incentives2(juror.0.clone(), juror.1)?; - result - }, - RevealedVote::No => { - let result = Self::winner_getting_incentives2( - juror.0.clone(), - winning_incentives, - juror.1, - )?; - result - }, - }, - WinningDecision::Draw => { - let result = - Self::getting_incentives_draw2(juror.0.clone(), juror.1)?; - result - }, - } - } - }, - Err(_) => {}, - } - } - // Remove SorititionSumTrees in `sortition-sum-game` pallet - let _result = T::SortitionSumGameSource::remove_tree_link(key.clone()); - - // Remove DrawnJurors - >::remove(&key); - - // Remove VoteCommits - >::remove_prefix(key.clone(), None); // Deprecated: Use clear_prefix instead - // let reveal_votes_iterator2 = >::iter_prefix(&key); - // reveal_votes_iterator2.for_each(|(account_id, _)|{ - // >::remove(key.clone(), account_id); - // }); - - Ok(()) - } - - // Improvements: Will it be better to distribute all jurors incentives in single call - pub(super) fn get_incentives_two_choice_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - who: AccountIdOf, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Execution, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - - let drawn_juror = >::get(&key); - - let who_commit_vote = >::get(&key, &who); - match who_commit_vote { - Some(commit_struct) => { - let vote_option = commit_struct.revealed_vote; - match vote_option { - Some(vote) => { - let decision_count: (u64, u64) = >::get(&key); - let incentives = phase_data.juror_incentives; - let (winning_decision, winning_incentives) = - Self::get_winning_incentives(decision_count, incentives); - if let Ok(i) = drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) { - let stake = drawn_juror[i].1; - match winning_decision { - WinningDecision::WinnerYes => match vote { - RevealedVote::Yes => { - let result = Self::winner_getting_incentives( - key.clone(), - who.clone(), - winning_incentives, - stake, - )?; - result - }, - RevealedVote::No => { - let result = Self::looser_getting_incentives( - key.clone(), - who.clone(), - stake, - )?; - result - }, - }, - WinningDecision::WinnerNo => match vote { - RevealedVote::Yes => { - let result = Self::looser_getting_incentives( - key.clone(), - who.clone(), - stake, - )?; - result - }, - RevealedVote::No => { - let result = Self::winner_getting_incentives( - key.clone(), - who.clone(), - winning_incentives, - stake, - )?; - result - }, - }, - WinningDecision::Draw => { - let result = Self::getting_incentives_draw( - key.clone(), - who.clone(), - stake.clone(), - )?; - result - }, - } - } else { - Err(Error::::StakeDoesNotExists)? - } - }, - None => Err(Error::::VoteNotRevealed)?, - } - }, - None => Err(Error::::CommitDoesNotExists)?, - } - Ok(()) - } - - pub(super) fn getting_incentives_draw( - key: SumTreeNameType, - who: AccountIdOf, - stake: u64, - ) -> DispatchResult { - let balance = Self::u64_to_balance_saturated(stake); - let mut juror_got_incentives = >::get(&key); - match juror_got_incentives.binary_search(&who) { - Ok(_) => Err(Error::::AlreadyGotIncentives)?, - Err(index) => { - juror_got_incentives.insert(index, who.clone()); - >::insert(&key, juror_got_incentives); - let r = T::Currency::deposit_into_existing(&who, balance).ok().unwrap(); - T::Reward::on_unbalanced(r); - }, - } - - Ok(()) - } - - pub(super) fn getting_incentives_draw2(who: AccountIdOf, stake: u64) -> DispatchResult { - let balance = Self::u64_to_balance_saturated(stake); - - let r = T::Currency::deposit_into_existing(&who, balance).ok().unwrap(); - T::Reward::on_unbalanced(r); - - Ok(()) - } - - pub(super) fn looser_getting_incentives( - key: SumTreeNameType, - who: AccountIdOf, - stake: u64, - ) -> DispatchResult { - let balance = Self::u64_to_balance_saturated(stake * 3 / 4); - let mut juror_got_incentives = >::get(&key); - match juror_got_incentives.binary_search(&who) { - Ok(_) => Err(Error::::AlreadyGotIncentives)?, - Err(index) => { - juror_got_incentives.insert(index, who.clone()); - >::insert(&key, juror_got_incentives); - let r = T::Currency::deposit_into_existing(&who, balance).ok().unwrap(); - T::Reward::on_unbalanced(r); - }, - } - Ok(()) - } - - pub(super) fn looser_getting_incentives2(who: AccountIdOf, stake: u64) -> DispatchResult { - let balance = Self::u64_to_balance_saturated(stake * 3 / 4); - - let r = T::Currency::deposit_into_existing(&who, balance).ok().unwrap(); - T::Reward::on_unbalanced(r); - - Ok(()) - } - - pub(super) fn winner_getting_incentives( - key: SumTreeNameType, - who: AccountIdOf, - winning_incentives: u64, - stake: u64, - ) -> DispatchResult { - let mut juror_got_incentives = >::get(&key); - match juror_got_incentives.binary_search(&who) { - Ok(_) => Err(Error::::AlreadyGotIncentives)?, - Err(index) => { - juror_got_incentives.insert(index, who.clone()); - >::insert(&key, juror_got_incentives); - let total_incentives = stake.checked_add(winning_incentives).expect("overflow"); - let incentives = Self::u64_to_balance_saturated(total_incentives); - let r = T::Currency::deposit_into_existing(&who, incentives).ok().unwrap(); - T::Reward::on_unbalanced(r); - }, - }; - - Ok(()) - } - - pub(super) fn winner_getting_incentives2( - who: AccountIdOf, - winning_incentives: u64, - stake: u64, - ) -> DispatchResult { - let total_incentives = stake.checked_add(winning_incentives).expect("overflow"); - let incentives = Self::u64_to_balance_saturated(total_incentives); - let r = T::Currency::deposit_into_existing(&who, incentives).ok().unwrap(); - T::Reward::on_unbalanced(r); - - Ok(()) - } - - pub(super) fn get_winning_decision(decision_tuple: (u64, u64)) -> WinningDecision { - if decision_tuple.1 > decision_tuple.0 { - WinningDecision::WinnerYes // Decision 1 won - } else if decision_tuple.0 > decision_tuple.1 { - WinningDecision::WinnerNo // Decision 0 won - } else { - WinningDecision::Draw // draw - } - } - - pub(super) fn get_winning_decision_value(key: SumTreeNameType) -> WinningDecision { - let decision_tuple: (u64, u64) = >::get(&key); - Self::get_winning_decision(decision_tuple) - } - - pub(super) fn get_winning_incentives( - decision_tuple: (u64, u64), - incentive_tuple: (u64, u64), - ) -> (WinningDecision, u64) { - let winning_decision = Self::get_winning_decision(decision_tuple); - match winning_decision { - WinningDecision::WinnerYes => { - let winning_incentives = - (incentive_tuple.1).checked_div(decision_tuple.1).expect("Overflow"); - (WinningDecision::WinnerYes, winning_incentives) - }, - WinningDecision::WinnerNo => { - let winning_incentives = - (incentive_tuple.1).checked_div(decision_tuple.0).expect("Overflow"); - (WinningDecision::WinnerNo, winning_incentives) - }, - WinningDecision::Draw => (WinningDecision::Draw, 0), - } - } - - pub(super) fn balance_to_u64_saturated(input: BalanceOf) -> u64 { - input.saturated_into::() - } - - pub(super) fn u64_to_balance_saturated(input: u64) -> BalanceOf { - input.saturated_into::>() - } - - pub(super) fn block_number_to_u32_saturated(input: BlockNumberOf) -> u32 { - input.saturated_into::() - } - pub(super) fn get_and_increment_nonce() -> Vec { - let nonce = >::get(); - >::put(nonce.wrapping_add(1)); - // let n = nonce * 1000 + 1000; // remove and uncomment in production - // n.encode() - - nonce.encode() - } - pub(super) fn get_evidence_period_end_block_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - now: BlockNumberOf, - ) -> Option { - let start_block_number = >::get(&key); - let evidence_length = phase_data.evidence_length; - let end_block = start_block_number.checked_add(&evidence_length).expect("Overflow"); - let left_block = end_block.checked_sub(&now); - match left_block { - Some(val) => { - let left_block_u32 = Self::block_number_to_u32_saturated(val); - Some(left_block_u32) - }, - None => Some(0), - } - } - - pub(super) fn get_staking_period_end_block_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - now: BlockNumberOf, - ) -> Option { - let staking_start_time = >::get(&key); - let staking_length = phase_data.staking_length; - let end_block = staking_start_time.checked_add(&staking_length).expect("Overflow"); - let left_block = end_block.checked_sub(&now); - match left_block { - Some(val) => { - let left_block_u32 = Self::block_number_to_u32_saturated(val); - Some(left_block_u32) - }, - None => Some(0), - } - } - - pub(super) fn get_drawing_period_end_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - ) -> (u64, u64, bool) { - let max_draws = phase_data.max_draws; - let draws_in_round = >::get(&key); - if draws_in_round >= max_draws.into() { - (max_draws, draws_in_round, true) - } else { - (max_draws, draws_in_round, false) - } - } - - pub(super) fn get_commit_period_end_block_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - now: BlockNumberOf, - ) -> Option { - let commit_start_time = >::get(&key); - let commit_length = phase_data.commit_length; - let end_block = commit_start_time.checked_add(&commit_length).expect("Overflow"); - let left_block = end_block.checked_sub(&now); - match left_block { - Some(val) => { - let left_block_u32 = Self::block_number_to_u32_saturated(val); - Some(left_block_u32) - }, - None => Some(0), - } - } - - pub(super) fn get_vote_period_end_block_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - now: BlockNumberOf, - ) -> Option { - let vote_start_time = >::get(&key); - let vote_length = phase_data.vote_length; - let end_block = vote_start_time.checked_add(&vote_length).expect("Overflow"); - let left_block = end_block.checked_sub(&now); - match left_block { - Some(val) => { - let left_block_u32 = Self::block_number_to_u32_saturated(val); - Some(left_block_u32) - }, - None => Some(0), - } - } - - pub(super) fn selected_as_juror_helper(key: SumTreeNameType, who: T::AccountId) -> bool { - let drawn_juror = >::get(&key); - match drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) { - Ok(_) => true, - Err(_) => false, - } - } -} diff --git a/pallets/schelling-game-shared/src/functions.rs b/pallets/schelling-game-shared/src/functions.rs deleted file mode 100644 index 889684f..0000000 --- a/pallets/schelling-game-shared/src/functions.rs +++ /dev/null @@ -1,114 +0,0 @@ -use super::*; - -// 6 sec (1 block) -// 3 days (43200), 10 days (144000) -// 15 mins (150) -// 5 mins (50) -// 8 mins (80) - -impl PhaseData { - pub fn new( - evidence_length: T::BlockNumber, - end_of_staking_time: T::BlockNumber, - staking_length: T::BlockNumber, - drawing_length: T::BlockNumber, - commit_length: T::BlockNumber, - vote_length: T::BlockNumber, - appeal_length: T::BlockNumber, - max_draws: u64, - min_number_juror_staked: u64, - min_juror_stake: BalanceOf, - juror_incentives: (u64, u64), - ) -> Self { - PhaseData { - evidence_length, - end_of_staking_time, - staking_length, - drawing_length, - commit_length, - vote_length, - appeal_length, - max_draws, - min_number_juror_staked, - min_juror_stake, - juror_incentives, - } - } - - pub fn default() -> Self { - PhaseData { - evidence_length: 144000u64.saturated_into::>(), - end_of_staking_time: 144000u64.saturated_into::>(), - staking_length: 144000u64.saturated_into::>(), - drawing_length: 144000u64.saturated_into::>(), - commit_length: 144000u64.saturated_into::>(), - vote_length: 144000u64.saturated_into::>(), - appeal_length: 144000u64.saturated_into::>(), - max_draws: 30, - min_number_juror_staked: 50, - min_juror_stake: 1000u64.saturated_into::>(), - juror_incentives: (1000, 1000), - } - } - - pub fn create_with_data( - block_length: u64, - max_draws: u64, - min_number_juror_staked: u64, - min_juror_stake: u64, - juror_incentives: (u64, u64), - ) -> Self { - let block_length = block_length.saturated_into::>(); - let min_juror_stake = min_juror_stake.saturated_into::>(); - PhaseData { - evidence_length: block_length, - end_of_staking_time: block_length, - staking_length: block_length, - drawing_length: block_length, - commit_length: block_length, - vote_length: block_length, - appeal_length: block_length, - max_draws, - min_number_juror_staked, - min_juror_stake, - juror_incentives, - } - } - - pub fn create_phase_with_all_data( - evidence_length: u64, - end_of_staking_time: u64, - staking_length: u64, - drawing_length: u64, - commit_length: u64, - vote_length: u64, - appeal_length: u64, - max_draws: u64, - min_number_juror_staked: u64, - min_juror_stake: u64, - juror_incentives: (u64, u64), - ) -> Self { - let evidence_length = evidence_length.saturated_into::>(); - let end_of_staking_time = end_of_staking_time.saturated_into::>(); - let staking_length = staking_length.saturated_into::>(); - let drawing_length = drawing_length.saturated_into::>(); - let commit_length = commit_length.saturated_into::>(); - let vote_length = vote_length.saturated_into::>(); - let appeal_length = appeal_length.saturated_into::>(); - - let min_juror_stake = min_juror_stake.saturated_into::>(); - PhaseData { - evidence_length, - end_of_staking_time, - staking_length, - drawing_length, - commit_length, - vote_length, - appeal_length, - max_draws, - min_number_juror_staked, - min_juror_stake, - juror_incentives, - } - } -} diff --git a/pallets/schelling-game-shared/src/lib.rs b/pallets/schelling-game-shared/src/lib.rs deleted file mode 100644 index 865089d..0000000 --- a/pallets/schelling-game-shared/src/lib.rs +++ /dev/null @@ -1,273 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -mod extras; -mod functions; -mod score_game; -mod share_link; -pub mod types; - -use crate::types::{ - CommitVote, Period, PhaseData, RangePoint, RevealedVote, SchellingGameType, ScoreCommitVote, - VoteStatus, WinningDecision, -}; -use frame_support::pallet_prelude::*; -use frame_support::sp_runtime::traits::{CheckedAdd, CheckedSub}; -use frame_support::sp_runtime::SaturatedConversion; -use frame_support::sp_std::prelude::*; -use frame_support::traits::Randomness; -use frame_support::traits::{Currency, OnUnbalanced, ReservableCurrency}; -use num_integer::Roots; -use scale_info::prelude::format; -use sortition_sum_game::types::SumTreeName; -use sortition_sum_game_link::SortitionSumGameLink; - -pub type BlockNumberOf = ::BlockNumber; -type AccountIdOf = ::AccountId; -type BalanceOf = <::Currency as Currency>>::Balance; -type PositiveImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::PositiveImbalance; -type NegativeImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::NegativeImbalance; -type SumTreeNameType = SumTreeName, BlockNumberOf>; -type PhaseDataOf = PhaseData; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: frame_system::Config { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - - type SortitionSumGameSource: SortitionSumGameLink< - SumTreeName = SumTreeName, - AccountId = Self::AccountId, - >; - - type Currency: ReservableCurrency; - - type RandomnessSource: Randomness; - - /// Handler for the unbalanced increment when rewarding (minting rewards) - type Reward: OnUnbalanced>; - - /// Handler for the unbalanced decrement when slashing (burning collateral) - type Slash: OnUnbalanced>; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::storage] - pub type Nonce = StorageValue<_, u64, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn get_period)] - pub type PeriodName = StorageMap<_, Blake2_128Concat, SumTreeNameType, Period>; - - #[pallet::storage] - #[pallet::getter(fn draws_in_round)] - pub type DrawsInRound = StorageMap<_, Blake2_128Concat, SumTreeNameType, u64, ValueQuery>; // A counter of draws made in the current round. - - #[pallet::storage] - #[pallet::getter(fn evidence_start_time)] - pub type EvidenceStartTime = - StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn staking_start_time)] - pub type StakingStartTime = - StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn commit_start_time)] - pub type CommitStartTime = - StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn vote_start_time)] - pub type VoteStartTime = - StorageMap<_, Blake2_128Concat, SumTreeNameType, BlockNumberOf, ValueQuery>; - - /// Drawn jurors containing account id and stake Vec<(AccountId, Stake)> - /// Should be stored in sorted order by AccountId - #[pallet::storage] - #[pallet::getter(fn drawn_jurors)] - pub type DrawnJurors = - StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec<(T::AccountId, u64)>, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn unstaked_jurors)] - pub type UnstakedJurors = - StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec, ValueQuery>; - - /// VoteCommits for Yes or No voting - #[pallet::storage] - #[pallet::getter(fn vote_commits)] - pub type VoteCommits = StorageDoubleMap< - _, - Blake2_128Concat, - SumTreeNameType, - Blake2_128Concat, - T::AccountId, - CommitVote, - >; - - /// Vote Commits for Score Schelling - #[pallet::storage] - #[pallet::getter(fn vote_commits_score)] - pub type ScoreVoteCommits = StorageDoubleMap< - _, - Blake2_128Concat, - SumTreeNameType, - Blake2_128Concat, - T::AccountId, - ScoreCommitVote, - >; - - /// Reveal values of score schelling game as Vec - #[pallet::storage] - #[pallet::getter(fn reveal_score_values)] - pub type RevealScoreValues = - StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec, ValueQuery>; - - /// New mean from the reveal values in score schelling game - /// Improvement: This step will not be required if all jurors incentives are distributed at one time - #[pallet::storage] - #[pallet::getter(fn new_mean_reveal_score)] - pub type IncentiveMeanRevealScore = - StorageMap<_, Blake2_128Concat, SumTreeNameType, i64, ValueQuery>; - - /// Decision count for two choices after reveal vote: (count for 0, count for 1) - #[pallet::storage] - #[pallet::getter(fn decision_count)] - pub type DecisionCount = - StorageMap<_, Blake2_128Concat, SumTreeNameType, (u64, u64), ValueQuery>; // Count for 0, Count for 1 - - #[pallet::storage] - #[pallet::getter(fn juror_incentive_distribution)] - pub type JurorsIncentiveDistributedAccounts = - StorageMap<_, Blake2_128Concat, SumTreeNameType, Vec, ValueQuery>; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { something: u32, who: T::AccountId }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - PeriodExists, - EvidencePeriodNotOver, - StakingPeriodNotOver, - PeriodIsNotEvidence, - PeriodIsNotNone, - MaxJurorNotDrawn, - CommitPeriodNotOver, - VotePeriodNotOver, - PeriodDoesNotExists, - PeriodDontMatch, - JurorStakeLessThanMin, - AlreadyStaked, - MaxDrawExceeded, - SelectedAsJuror, - AlreadyUnstaked, - StakeDoesNotExists, - JurorDoesNotExists, - VoteStatusNotCommited, - NotValidChoice, - CommitDoesNotMatch, - CommitDoesNotExists, - AlreadyGotIncentives, - VoteNotRevealed, - TimeForStakingOver, - TimeForStakingNotOver, - } - - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. - #[pallet::call] - impl Pallet { - /// An example dispatchable that takes a singles value as a parameter, writes the value to - /// storage and emits an event. This function must be dispatched by a signed extrinsic. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::do_something())] - pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - // This function will return an error if the extrinsic is not signed. - // https://docs.substrate.io/main-docs/build/origins/ - let who = ensure_signed(origin)?; - - // Update storage. - >::put(something); - - // Emit an event. - Self::deposit_event(Event::SomethingStored { something, who }); - // Return a successful DispatchResultWithPostInfo - Ok(()) - } - - /// An example dispatchable that may throw a custom error. - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::cause_error())] - pub fn cause_error(origin: OriginFor) -> DispatchResult { - let _who = ensure_signed(origin)?; - - // Read a value from storage. - match >::get() { - // Return an error if the value has not been set. - None => return Err(Error::::NoneValue.into()), - Some(old) => { - // Increment the value read from storage; will error in the event of overflow. - let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; - // Update the value in storage with the incremented result. - >::put(new); - Ok(()) - }, - } - } - } -} diff --git a/pallets/schelling-game-shared/src/mock.rs b/pallets/schelling-game-shared/src/mock.rs deleted file mode 100644 index eb95599..0000000 --- a/pallets/schelling-game-shared/src/mock.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate as pallet_template; -use frame_support::traits::{ConstU16, ConstU64}; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; -use frame_support_test::TestRandomness; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - TemplateModule: pallet_template, - Balances: pallet_balances, - SortitionSumGame: sortition_sum_game, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - type AccountData = pallet_balances::AccountData; // New code -} - -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Currency = Balances; // New code - type RandomnessSource = TestRandomness; - type Slash = (); - type Reward = (); - type SortitionSumGameSource = SortitionSumGame; -} - -impl sortition_sum_game::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU64<1>; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type MaxHolds = (); - type HoldIdentifier = (); - type AccountStore = System; -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 100000), - (2, 200000), - (3, 300000), - (4, 300000), - (5, 300000), - (6, 300000), - (7, 300000), - (8, 300000), - (9, 300000), - (10, 300000), - (11, 300000), - (12, 300000), - (13, 300000), - (14, 300000), - (15, 300000), - (16, 300000), - (17, 300000), - (18, 300000), - (19, 300000), - (20, 300000), - (21, 300000), - (22, 300000), - (23, 300000), - (24, 300000), - (25, 300000), - (26, 300000), - (27, 300000), - (28, 300000), - (29, 300000), - (30, 300000), - (31, 300000), - (32, 300000), - (33, 300000), - (34, 300000), - (35, 300000), - ], - } // new code - .assimilate_storage(&mut t) - .unwrap(); - t.into() -} diff --git a/pallets/schelling-game-shared/src/score_game.rs b/pallets/schelling-game-shared/src/score_game.rs deleted file mode 100644 index 07971ff..0000000 --- a/pallets/schelling-game-shared/src/score_game.rs +++ /dev/null @@ -1,223 +0,0 @@ -use crate::*; -use scale_info::prelude::vec; - -impl Pallet { - /// Commit your score vote - pub(super) fn commit_vote_for_score_helper( - key: SumTreeNameType, - who: AccountIdOf, - vote_commit: [u8; 32], - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Commit, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - let drawn_jurors = >::get(&key); - match drawn_jurors.binary_search_by(|(c, _)| c.cmp(&who.clone())) { - Ok(_) => { - let vote_commit_struct = ScoreCommitVote { - commit: vote_commit, - votestatus: VoteStatus::Commited, - revealed_vote: None, - }; - >::insert(&key, &who, vote_commit_struct); - }, - Err(_) => Err(Error::::JurorDoesNotExists)?, - } - Ok(()) - } - - /// choice is i64. Validate the range of the choice while using the function - pub(super) fn reveal_vote_score_helper( - key: SumTreeNameType, - who: AccountIdOf, - choice: i64, - salt: Vec, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Vote, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - let who_commit_vote = >::get(&key, &who); - match who_commit_vote { - Some(mut commit_struct) => { - ensure!( - commit_struct.votestatus == VoteStatus::Commited, - Error::::VoteStatusNotCommited - ); - let mut vote = format!("{}", choice).as_bytes().to_vec(); - // let mut vote = choice.clone(); - let mut salt_a = salt.clone(); - vote.append(&mut salt_a); - let vote_bytes: &[u8] = &vote; - let hash = sp_io::hashing::keccak_256(vote_bytes); - let commit: &[u8] = &commit_struct.commit; - if hash == commit { - let mut reveal_score_values = >::get(&key); - reveal_score_values.push(choice * 1000); - >::insert(&key, reveal_score_values); - commit_struct.revealed_vote = Some(choice); - commit_struct.votestatus = VoteStatus::Revealed; - >::insert(&key, &who, commit_struct); - } else { - Err(Error::::CommitDoesNotMatch)? - } - }, - None => Err(Error::::CommitDoesNotExists)?, - } - - Ok(()) - } - - /// Distribute incentives to all jurors in execution period in score schelling game - pub(super) fn get_incentives_score_schelling_helper( - key: SumTreeNameType, - phase_data: PhaseDataOf, - range_point: RangePoint, - ) -> DispatchResult { - match >::get(&key) { - Some(period) => { - ensure!(period == Period::Execution, Error::::PeriodDontMatch); - }, - None => Err(Error::::PeriodDoesNotExists)?, - } - - let drawn_jurors = >::get(&key); - let reveal_votes_iterator = >::iter_prefix(&key); - let reveal_values = >::get(&key); - let sd_and_mean = Self::std_deviation_interger(&reveal_values); - let new_mean = Self::calculate_new_mean(&reveal_values, sd_and_mean).unwrap(); - // println!("new mean: {:?}", new_mean); - >::insert(key.clone(), new_mean); - let incentives_range = Self::get_incentives_range(range_point); - let mut reveal_votes = reveal_votes_iterator - .map(|(account_id, score_commit_vote)| (account_id, score_commit_vote.revealed_vote)) - .collect::>(); - reveal_votes.sort_by(|a, b| a.0.cmp(&b.0)); - - // println!("reveal votes, {:?}",reveal_votes); - let mut winners = vec![]; - for juror in drawn_jurors { - match reveal_votes.binary_search_by(|(c, _)| c.cmp(&juror.0)) { - Ok(index) => { - // println!("Ok index {:?}", index); - let account_n_vote = &reveal_votes[index]; - if let Some(i) = account_n_vote.1 { - // println!("vote {:?}", i); - if i * 1000 >= new_mean.checked_sub(incentives_range).unwrap() - && i * 1000 <= new_mean.checked_add(incentives_range).unwrap() - { - // get incentives - winners.push((juror.0.clone(), juror.1.clone())); - } else { - // deduct incentives - let stake = juror.1; - let balance = Self::u64_to_balance_saturated(stake * 3 / 4); - let r = - T::Currency::deposit_into_existing(&juror.0, balance).ok().unwrap(); - T::Reward::on_unbalanced(r); - } - } - }, - Err(_) => { - // println!("Err index {:?}", index); - }, - } - } - - let winners_len = winners.len() as u64; - // println!("winners_len {}", winners_len); - let incentives_tuple = phase_data.juror_incentives; - let winning_incentives = incentives_tuple.1.checked_div(winners_len).expect("oveflow"); - for winner in winners { - let total_incentives = winner.1.checked_add(winning_incentives).expect("overflow"); - let incentives = Self::u64_to_balance_saturated(total_incentives); - let r = T::Currency::deposit_into_existing(&winner.0, incentives).ok().unwrap(); - T::Reward::on_unbalanced(r); - } - - // Remove all data - - // Remove SorititionSumTrees in `sortition-sum-game` pallet - let _result = T::SortitionSumGameSource::remove_tree_link(key.clone()); - - // Remove DrawnJurors - >::remove(&key); - - // Remove UnstakedJurors (all jurors can be returned their incentives at a time) - - // Remove ScoreVoteCommits - >::remove_prefix(key.clone(), None); // Deprecated: Use clear_prefix instead - // let reveal_votes_iterator2 = >::iter_prefix(&key); - // reveal_votes_iterator2.for_each(|(account_id, _)|{ - // >::remove(key.clone(), account_id); - // }); - - // Remove RevealScoreValues - >::remove(&key); - - Ok(()) - } - - pub(super) fn get_mean_value(key: SumTreeNameType) -> i64 { - let value = >::get(key.clone()); - value - } - - /// Calculate the mean of integer - pub(super) fn mean_integer(data: &Vec) -> Option { - let data_mul_sum = data.iter().sum::(); - let count = data.len(); - - match count { - positive if positive > 0 => Some(data_mul_sum / count as i64), - _ => None, - } - } - - pub(super) fn std_deviation_interger(data: &Vec) -> Option<(i64, i64)> { - let mean = Self::mean_integer(data); - match (mean, data.len()) { - (Some(data_mean), count) if count > 0 => { - let variance = data - .iter() - .map(|value| { - let diff = data_mean.checked_sub(*value as i64).unwrap(); - diff * diff - }) - .sum::() / count as i64; - - Some((variance.sqrt(), mean.unwrap())) - }, - _ => None, - } - } - - pub(super) fn calculate_new_mean( - data: &Vec, - sd_and_mean: Option<(i64, i64)>, - ) -> Option { - let mut new_items = vec![]; - let mean = sd_and_mean.unwrap().1; - let sd = sd_and_mean.unwrap().0; - for x in data { - if *x >= mean.checked_sub(sd).unwrap() && *x <= mean.checked_add(sd).unwrap() { - new_items.push(*x); - } - } - let new_mean = Self::mean_integer(&new_items); - new_mean - } - - pub(super) fn get_incentives_range(range_point: RangePoint) -> i64 { - match range_point { - RangePoint::ZeroToTen => 1500, //3 points, 1.5 ± mean, multiply by 1000 to make it integer - RangePoint::MinusTenToPlusTen => 3000, //6 points, 3 ± mean - RangePoint::ZeroToFive => 750, //1.5 points, 0.75 ± mean - } - } -} diff --git a/pallets/schelling-game-shared/src/share_link.rs b/pallets/schelling-game-shared/src/share_link.rs deleted file mode 100644 index e2ac668..0000000 --- a/pallets/schelling-game-shared/src/share_link.rs +++ /dev/null @@ -1,308 +0,0 @@ -use crate::*; - -use schelling_game_shared_link::SchellingGameSharedLink; - -impl SchellingGameSharedLink for Pallet { - type SumTreeName = SumTreeNameType; - type SchellingGameType = SchellingGameType; - type BlockNumber = BlockNumberOf; - type AccountId = AccountIdOf; - type Balance = BalanceOf; - type RangePoint = RangePoint; - type Period = Period; - type PhaseData = PhaseDataOf; - type WinningDecision = WinningDecision; - - fn create_phase_data( - block_length: u64, - max_draws: u64, - min_number_juror_staked: u64, - min_juror_stake: u64, - juror_incentives: (u64, u64), - ) -> Self::PhaseData { - Self::create_phase_data( - block_length, - max_draws, - min_number_juror_staked, - min_juror_stake, - juror_incentives, - ) - } - - fn create_phase_with_all_data( - evidence_length: u64, - end_of_staking_time: u64, - staking_length: u64, - drawing_length: u64, - commit_length: u64, - vote_length: u64, - appeal_length: u64, - max_draws: u64, - min_number_juror_staked: u64, - min_juror_stake: u64, - juror_incentives: (u64, u64), - ) -> Self::PhaseData { - Self::create_phase_with_all_data( - evidence_length, - end_of_staking_time, - staking_length, - drawing_length, - commit_length, - vote_length, - appeal_length, - max_draws, - min_number_juror_staked, - min_juror_stake, - juror_incentives, - ) - } - - /// Get the Period - fn get_period_link(key: Self::SumTreeName) -> Option { - Self::get_period(key) - } - - /// Set `PeriodName` to `Period::Evidence` - /// Called with submission of `Evidence` stake e.g. Profile stake - /// Also set `EvidenceStartTime` - fn set_to_evidence_period_link( - key: Self::SumTreeName, - now: Self::BlockNumber, - ) -> DispatchResult { - Self::set_to_evidence_period(key, now) - } - - /// Create a sortition sum tree - fn create_tree_helper_link(key: Self::SumTreeName, k: u64) -> DispatchResult { - Self::create_tree_link_helper(key, k) - } - - /// Check `Period` is `Evidence`, and change it to `Staking` - /// It is called with function that submits challenge stake after `end_block` of evidence period - /// Checks evidence period is over - #[doc=include_str!("docimage/set_to_staking_period_1.svg")] - /// ```ignore - /// if time >= block_time.min_short_block_length { - /// // change `Period` to `Staking` - /// } - /// ``` - fn set_to_staking_period_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - now: Self::BlockNumber, - ) -> DispatchResult { - Self::set_to_staking_period(key, phase_data, now) - } - - fn ensure_time_for_staking_over_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - now: Self::BlockNumber, - ) -> DispatchResult { - Self::ensure_time_for_staking_over(key, phase_data, now) - } - - fn set_to_staking_period_pe_link( - key: Self::SumTreeName, - now: Self::BlockNumber, - ) -> DispatchResult { - Self::set_to_staking_period_pe(key, now) - } - - /// Change the `Period` - /// - /// `Period::Staking` to `Period::Drawing` - #[doc=include_str!("docimage/change_period_link_1.svg")] - /// ```ignore - /// if now >= min_long_block_length + staking_start_time { - /// // Change `Period::Staking` to `Period::Drawing` - /// } - /// ``` - /// - /// `Period::Drawing` to `Period::Commit` - /// When maximum juror are drawn - /// - /// `Period::Commit` to `Period::Vote` - /// ```ignore - /// if now >= min_long_block_length + commit_start_time { - /// // Change `Period::Commit` to `Period::Vote` - /// } - /// ``` - /// - /// `Period::Vote` to `Period::Execution` - /// ```ignore - /// if now >= min_long_block_length + vote_start_time { - /// // Change `Period::Vote` to `Period::Execution` - /// } - /// ``` - fn change_period_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - now: Self::BlockNumber, - ) -> DispatchResult { - Self::change_period(key, phase_data, now) - } - - /// Apply Jurors - /// Ensure `Period` is `Staking` - /// Slash the stake. - /// Store the stake on sortition sum tree if doesn't exists. - fn apply_jurors_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - who: Self::AccountId, - stake: Self::Balance, - ) -> DispatchResult { - Self::apply_jurors_helper(key, phase_data, who, stake) - } - - /// Draw Jurors - /// Ensure `Period` is `Drawing` - /// `iterations` is number of jurors drawn per call - /// Ensure total draws `draws_in_round` is less than `max_draws` - fn draw_jurors_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - iterations: u64, - ) -> DispatchResult { - Self::draw_jurors_helper(key, phase_data, iterations) - } - - /// Unstake those who are not drawn as jurors - /// They can withdraw their stake - fn unstaking_helper_link(key: Self::SumTreeName, who: Self::AccountId) -> DispatchResult { - Self::unstaking_helper(key, who) - } - - /// Commit vote - fn commit_vote_helper_link( - key: Self::SumTreeName, - who: Self::AccountId, - vote_commit: [u8; 32], - ) -> DispatchResult { - Self::commit_vote_helper(key, who, vote_commit) - } - - /// Reveal vote - /// There are two vote choices 0 or 1 - fn reveal_vote_two_choice_helper_link( - key: Self::SumTreeName, - who: Self::AccountId, - choice: u128, - salt: Vec, - ) -> DispatchResult { - Self::reveal_vote_two_choice_helper(key, who, choice, salt) - } - /// Distribute incentives for two choices - /// Winner gets `stake` + `winning_incentives` - /// If decision is draw, jurors receive their `stake` - /// Lost jurors gets `stake * 3/4` - /// When they receive their incentives, their accountid is stored in `JurorsIncentiveDistributedAccounts` - fn get_incentives_two_choice_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - who: Self::AccountId, - ) -> DispatchResult { - Self::get_incentives_two_choice_helper(key, phase_data, who) - } - - /// Blocks left for ending evidence period - /// When evidence time ends, you can submit the challenge stake - /// `start_block_number` evidence start time which you will get from `EvidenceStartTime` - fn get_evidence_period_end_block_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - now: Self::BlockNumber, - ) -> Option { - Self::get_evidence_period_end_block_helper(key, phase_data, now) - } - - /// Blocks left for ending staking period - fn get_staking_period_end_block_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - now: Self::BlockNumber, - ) -> Option { - Self::get_staking_period_end_block_helper(key, phase_data, now) - } - - /// Return true when drawing period is over, otherwise false - fn get_drawing_period_end_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - ) -> (u64, u64, bool) { - Self::get_drawing_period_end_helper(key, phase_data) - } - - /// Blocks left for ending drawing period - fn get_commit_period_end_block_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - now: Self::BlockNumber, - ) -> Option { - Self::get_commit_period_end_block_helper(key, phase_data, now) - } - - /// Blocks left for ending vote period - fn get_vote_period_end_block_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - now: Self::BlockNumber, - ) -> Option { - Self::get_vote_period_end_block_helper(key, phase_data, now) - } - - /// Check if `AccountId` is selected as juror - fn selected_as_juror_helper_link(key: Self::SumTreeName, who: Self::AccountId) -> bool { - Self::selected_as_juror_helper(key, who) - } - - /// Commit vote for score schelling game - fn commit_vote_for_score_helper_link( - key: Self::SumTreeName, - who: Self::AccountId, - vote_commit: [u8; 32], - ) -> DispatchResult { - Self::commit_vote_for_score_helper(key, who, vote_commit) - } - - /// Reveal vote for score schelling game - fn reveal_vote_score_helper_link( - key: Self::SumTreeName, - who: Self::AccountId, - choice: i64, - salt: Vec, - ) -> DispatchResult { - Self::reveal_vote_score_helper(key, who, choice, salt) - } - - /// Distribute incentives to all score schelling game jurors - fn get_incentives_score_schelling_helper_link( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - range_point: Self::RangePoint, - ) -> DispatchResult { - Self::get_incentives_score_schelling_helper(key, phase_data, range_point) - } - - /// Get new mean in score schelling game - fn get_mean_value_link(key: Self::SumTreeName) -> i64 { - Self::get_mean_value(key) - } - - /// Distribute incentives to all two choice shelling game jurors - fn get_all_incentives_two_choice_helper( - key: Self::SumTreeName, - phase_data: Self::PhaseData, - ) -> DispatchResult { - Self::get_all_incentives_two_choice_helper(key, phase_data) - } - - fn get_drawn_jurors(key: Self::SumTreeName) -> Vec<(Self::AccountId, u64)> { - Self::drawn_jurors(key) - } - - fn get_winning_decision_value_link(key: Self::SumTreeName) -> WinningDecision { - Self::get_winning_decision_value(key) - } -} diff --git a/pallets/schelling-game-shared/src/tests.rs b/pallets/schelling-game-shared/src/tests.rs deleted file mode 100644 index d0ca20e..0000000 --- a/pallets/schelling-game-shared/src/tests.rs +++ /dev/null @@ -1,720 +0,0 @@ -use crate::{ - mock::*, - types::{Period, PhaseData, RangePoint, SchellingGameType}, - Error, Event, -}; -use frame_support::{assert_noop, assert_ok}; - -use sortition_sum_game::types::SumTreeName; - -type CitizenId = u64; - -fn return_key_profile(citizen_id: CitizenId) -> SumTreeName { - let key = SumTreeName::ProfileValidation { citizen_address: citizen_id, block_number: 10 }; - key -} - -fn return_game_type_profile_approval() -> SchellingGameType { - SchellingGameType::ProfileApproval -} - -fn get_the_phase_data() -> PhaseData { - let data = PhaseData::create_with_data(50, 5, 3, 100, (100, 100)); - data -} - -#[test] -fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - // Dispatch a signed extrinsic. - assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); - // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); - // Assert that the correct event was deposited - System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into()); - }); -} - -#[test] -fn correct_error_for_none_value() { - new_test_ext().execute_with(|| { - // Ensure the expected error is thrown when no value is present. - assert_noop!( - TemplateModule::cause_error(RuntimeOrigin::signed(1)), - Error::::NoneValue - ); - }); -} - -#[test] -fn evidence_period_not_over_test() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let phase_data = get_the_phase_data(); - let now2 = now + phase_data.evidence_length - 1; - assert_noop!( - TemplateModule::set_to_staking_period(key.clone(), phase_data, now2), - Error::::EvidencePeriodNotOver - ); - }); -} - -/// 1) Set evidence period -/// 2) Set staking period -/// 3) Create tree -#[test] -fn evidence_period_test() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let phase_data = get_the_phase_data(); - let now2 = now + phase_data.evidence_length; - assert_ok!(TemplateModule::set_to_staking_period(key.clone(), phase_data, now2)); - // Create tree - assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); - }); -} - -/// End of staking period - -#[test] -fn end_of_time_staking_period() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let phase_data = get_the_phase_data(); - let now2 = now + phase_data.evidence_length + phase_data.end_of_staking_time - 1; - assert_ok!(TemplateModule::set_to_staking_period(key.clone(), phase_data, now2)); - let phase_data = get_the_phase_data(); - let now2 = now + phase_data.evidence_length + phase_data.end_of_staking_time; - assert_noop!( - TemplateModule::set_to_staking_period(key.clone(), phase_data, now2), - Error::::PeriodIsNotEvidence - ); - }); -} - -/// Check time for staking over -#[test] -fn check_time_for_staking_not_over_test() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let phase_data = get_the_phase_data(); - let now2 = now + phase_data.evidence_length + phase_data.end_of_staking_time - 1; - assert_noop!( - TemplateModule::ensure_time_for_staking_over(key.clone(), phase_data, now2), - Error::::TimeForStakingNotOver - ); - let phase_data = get_the_phase_data(); - let now = now + phase_data.evidence_length + phase_data.end_of_staking_time; - assert_ok!(TemplateModule::ensure_time_for_staking_over(key.clone(), phase_data, now)); - }); -} - -#[test] -fn apply_juror() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let phase_data = get_the_phase_data(); - let now2 = now + phase_data.evidence_length; - assert_ok!(TemplateModule::set_to_staking_period(key.clone(), phase_data.clone(), now2)); - // Create tree - assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); - // Check the period is staking - let period = TemplateModule::get_period(key.clone()); - // println!("{:?}", period); - assert_eq!(Some(Period::Staking), period); - // Applyjuror - for j in 4..30 { - assert_ok!(TemplateModule::apply_jurors_helper( - key.clone(), - phase_data.clone(), - j, - j * 100 - )); - } - }); -} - -#[test] -fn challenger_win_test() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let phase_data = get_the_phase_data(); - - let staking_start_time = now + phase_data.evidence_length; - assert_ok!(TemplateModule::set_to_staking_period( - key.clone(), - phase_data.clone(), - staking_start_time - )); - // Create tree - assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); - // Check the period is staking - let period = TemplateModule::get_period(key.clone()); - // println!("{:?}", period); - assert_eq!(Some(Period::Staking), period); - // Applyjuror - for j in 4..30 { - assert_ok!(TemplateModule::apply_jurors_helper( - key.clone(), - phase_data.clone(), - j, - j * 100 - )); - } - let new_now = staking_start_time + phase_data.staking_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Drawing), period); - assert_ok!(TemplateModule::draw_jurors_helper(key.clone(), phase_data.clone(), 5)); - let draws_in_round = TemplateModule::draws_in_round(key.clone()); - assert_eq!(5, draws_in_round); - let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); - assert_eq!(vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], drawn_jurors); - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let balance = Balances::free_balance(5); - assert_eq!(299500, balance); - assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); - let balance = Balances::free_balance(5); - assert_eq!(300000, balance); - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 4, hash)); - let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 7, hash)); - let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 13, hash)); - let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 14, hash)); - let hash = sp_io::hashing::keccak_256("0salt5".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 15, hash)); - let commit_start_time = TemplateModule::commit_start_time(key.clone()); - let new_now = commit_start_time + phase_data.commit_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Vote), period); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 4, - 1, - "salt".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 7, - 1, - "salt2".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 13, - 1, - "salt3".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 14, - 1, - "salt4".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 15, - 0, - "salt5".as_bytes().to_vec() - )); - let decision = TemplateModule::decision_count(key.clone()); - assert_eq!((1, 4), decision); - let vote_start_time = TemplateModule::vote_start_time(key.clone()); - let new_now = vote_start_time + phase_data.vote_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Execution), period); - - let balance = Balances::free_balance(4); - assert_eq!(299600, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 4 - )); - let balance = Balances::free_balance(4); - assert_eq!(300025, balance); - let balance = Balances::free_balance(7); - // println!("{:?}", balance); - assert_eq!(299300, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 7 - )); - let balance = Balances::free_balance(7); - assert_eq!(300025, balance); - let balance = Balances::free_balance(13); - assert_eq!(298700, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 13 - )); - let balance = Balances::free_balance(13); - assert_eq!(300025, balance); - let balance = Balances::free_balance(14); - assert_eq!(298600, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 14 - )); - let balance = Balances::free_balance(14); - assert_eq!(300025, balance); - let balance = Balances::free_balance(15); - assert_eq!(298500, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 15 - )); - let balance = Balances::free_balance(15); - assert_eq!(299625, balance); - }); -} - -#[test] -fn challenger_win_test_jurors_incentive_in_one_go() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let game_type = return_game_type_profile_approval(); - - // let min_short_block_length = return_min_short_block_length(); - // let min_long_block_length = return_min_long_block_length(); - - let phase_data = get_the_phase_data(); - - let staking_start_time = now + phase_data.staking_length; - assert_ok!(TemplateModule::set_to_staking_period( - key.clone(), - phase_data.clone(), - staking_start_time - )); - // Create tree - assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); - // Check the period is staking - let period = TemplateModule::get_period(key.clone()); - // println!("{:?}", period); - assert_eq!(Some(Period::Staking), period); - // Applyjuror - for j in 4..30 { - assert_ok!(TemplateModule::apply_jurors_helper( - key.clone(), - phase_data.clone(), - j, - j * 100 - )); - } - let new_now = staking_start_time + phase_data.staking_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Drawing), period); - assert_ok!(TemplateModule::draw_jurors_helper(key.clone(), phase_data.clone(), 5)); - let draws_in_round = TemplateModule::draws_in_round(key.clone()); - assert_eq!(5, draws_in_round); - let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); - assert_eq!(vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], drawn_jurors); - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let balance = Balances::free_balance(5); - assert_eq!(299500, balance); - assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); - let balance = Balances::free_balance(5); - assert_eq!(300000, balance); - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 4, hash)); - let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 7, hash)); - let hash = sp_io::hashing::keccak_256("1salt3".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 13, hash)); - let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 14, hash)); - let hash = sp_io::hashing::keccak_256("0salt5".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 15, hash)); - let commit_start_time = TemplateModule::commit_start_time(key.clone()); - let new_now = commit_start_time + phase_data.commit_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Vote), period); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 4, - 1, - "salt".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 7, - 1, - "salt2".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 13, - 1, - "salt3".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 14, - 1, - "salt4".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 15, - 0, - "salt5".as_bytes().to_vec() - )); - let decision = TemplateModule::decision_count(key.clone()); - assert_eq!((1, 4), decision); - let vote_start_time = TemplateModule::vote_start_time(key.clone()); - let new_now = vote_start_time + phase_data.vote_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Execution), period); - let balance = Balances::free_balance(4); - assert_eq!(299600, balance); - let balance = Balances::free_balance(7); - // println!("{:?}", balance); - assert_eq!(299300, balance); - let balance = Balances::free_balance(13); - assert_eq!(298700, balance); - let balance = Balances::free_balance(14); - assert_eq!(298600, balance); - let balance = Balances::free_balance(15); - assert_eq!(298500, balance); - assert_ok!(TemplateModule::get_all_incentives_two_choice_helper( - key.clone(), - phase_data.clone() - )); - let balance = Balances::free_balance(4); - assert_eq!(300025, balance); - let balance = Balances::free_balance(7); - assert_eq!(300025, balance); - let balance = Balances::free_balance(13); - assert_eq!(300025, balance); - let balance = Balances::free_balance(14); - assert_eq!(300025, balance); - let balance = Balances::free_balance(15); - assert_eq!(299625, balance); - }); -} - -#[test] -fn challenger_lost_test() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - // let game_type = return_game_type_profile_approval(); - let phase_data = get_the_phase_data(); - - // let min_short_block_length = return_min_short_block_length(); - // let min_long_block_length = return_min_long_block_length(); - let staking_start_time = now + phase_data.staking_length; - assert_ok!(TemplateModule::set_to_staking_period( - key.clone(), - phase_data.clone(), - staking_start_time - )); - // Create tree - assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); - // Check the period is staking - let period = TemplateModule::get_period(key.clone()); - // println!("{:?}", period); - assert_eq!(Some(Period::Staking), period); - // Applyjuror - for j in 4..30 { - assert_ok!(TemplateModule::apply_jurors_helper( - key.clone(), - phase_data.clone(), - j, - j * 100 - )); - } - let new_now = staking_start_time + phase_data.staking_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Drawing), period); - assert_ok!(TemplateModule::draw_jurors_helper(key.clone(), phase_data.clone(), 5)); - let draws_in_round = TemplateModule::draws_in_round(key.clone()); - assert_eq!(5, draws_in_round); - let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); - assert_eq!(vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], drawn_jurors); - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let balance = Balances::free_balance(5); - assert_eq!(299500, balance); - assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); - let balance = Balances::free_balance(5); - assert_eq!(300000, balance); - let hash = sp_io::hashing::keccak_256("0salt".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 4, hash)); - let hash = sp_io::hashing::keccak_256("0salt2".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 7, hash)); - let hash = sp_io::hashing::keccak_256("0salt3".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 13, hash)); - let hash = sp_io::hashing::keccak_256("0salt4".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 14, hash)); - let hash = sp_io::hashing::keccak_256("1salt5".as_bytes()); - assert_ok!(TemplateModule::commit_vote_helper(key.clone(), 15, hash)); - let commit_start_time = TemplateModule::commit_start_time(key.clone()); - let new_now = commit_start_time + phase_data.commit_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Vote), period); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 4, - 0, - "salt".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 7, - 0, - "salt2".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 13, - 0, - "salt3".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 14, - 0, - "salt4".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_two_choice_helper( - key.clone(), - 15, - 1, - "salt5".as_bytes().to_vec() - )); - let decision = TemplateModule::decision_count(key.clone()); - assert_eq!((4, 1), decision); - let vote_start_time = TemplateModule::vote_start_time(key.clone()); - let new_now = vote_start_time + phase_data.vote_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Execution), period); - - let balance = Balances::free_balance(4); - assert_eq!(299600, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 4 - )); - let balance = Balances::free_balance(4); - assert_eq!(300025, balance); - let balance = Balances::free_balance(7); - // println!("{:?}", balance); - assert_eq!(299300, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 7 - )); - let balance = Balances::free_balance(7); - assert_eq!(300025, balance); - let balance = Balances::free_balance(13); - assert_eq!(298700, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 13 - )); - let balance = Balances::free_balance(13); - assert_eq!(300025, balance); - let balance = Balances::free_balance(14); - assert_eq!(298600, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 14 - )); - let balance = Balances::free_balance(14); - assert_eq!(300025, balance); - let balance = Balances::free_balance(15); - assert_eq!(298500, balance); - assert_ok!(TemplateModule::get_incentives_two_choice_helper( - key.clone(), - phase_data.clone(), - 15 - )); - let balance = Balances::free_balance(15); - assert_eq!(299625, balance); - }); -} - -#[test] -fn score_schelling_game_test() { - new_test_ext().execute_with(|| { - let key = return_key_profile(0); - let now = 10; - assert_ok!(TemplateModule::set_to_evidence_period(key.clone(), now)); - assert_eq!(TemplateModule::get_period(&key).unwrap(), Period::Evidence); - let game_type = return_game_type_profile_approval(); - // let min_short_block_length = return_min_short_block_length(); - // let min_long_block_length = return_min_long_block_length(); - let phase_data = get_the_phase_data(); - - let staking_start_time = now + phase_data.staking_length; - assert_ok!(TemplateModule::set_to_staking_period( - key.clone(), - phase_data.clone(), - staking_start_time - )); - // Create tree - assert_ok!(TemplateModule::create_tree_link_helper(key.clone(), 3)); - // Check the period is staking - let period = TemplateModule::get_period(key.clone()); - // println!("{:?}", period); - assert_eq!(Some(Period::Staking), period); - // Applyjuror - for j in 4..30 { - assert_ok!(TemplateModule::apply_jurors_helper( - key.clone(), - phase_data.clone(), - j, - j * 100 - )); - } - let new_now = staking_start_time + phase_data.staking_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Drawing), period); - assert_ok!(TemplateModule::draw_jurors_helper(key.clone(), phase_data.clone(), 5)); - let draws_in_round = TemplateModule::draws_in_round(key.clone()); - assert_eq!(5, draws_in_round); - let drawn_jurors = TemplateModule::drawn_jurors(key.clone()); - assert_eq!(vec![(4, 400), (7, 700), (13, 1300), (14, 1400), (15, 1500)], drawn_jurors); - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let balance = Balances::free_balance(5); - assert_eq!(299500, balance); - assert_ok!(TemplateModule::unstaking_helper(key.clone(), 5)); - let balance = Balances::free_balance(5); - assert_eq!(300000, balance); - let hash = sp_io::hashing::keccak_256("1salt".as_bytes()); - assert_ok!(TemplateModule::commit_vote_for_score_helper(key.clone(), 4, hash)); - let hash = sp_io::hashing::keccak_256("1salt2".as_bytes()); - assert_ok!(TemplateModule::commit_vote_for_score_helper(key.clone(), 7, hash)); - let hash = sp_io::hashing::keccak_256("5salt3".as_bytes()); - assert_ok!(TemplateModule::commit_vote_for_score_helper(key.clone(), 13, hash)); - let hash = sp_io::hashing::keccak_256("1salt4".as_bytes()); - assert_ok!(TemplateModule::commit_vote_for_score_helper(key.clone(), 14, hash)); - let hash = sp_io::hashing::keccak_256("7salt5".as_bytes()); - assert_ok!(TemplateModule::commit_vote_for_score_helper(key.clone(), 15, hash)); - let commit_start_time = TemplateModule::commit_start_time(key.clone()); - let new_now = commit_start_time + phase_data.commit_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Vote), period); - assert_ok!(TemplateModule::reveal_vote_score_helper( - key.clone(), - 4, - 1, - "salt".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_score_helper( - key.clone(), - 7, - 1, - "salt2".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_score_helper( - key.clone(), - 13, - 5, - "salt3".as_bytes().to_vec() - )); - assert_ok!(TemplateModule::reveal_vote_score_helper( - key.clone(), - 14, - 1, - "salt4".as_bytes().to_vec() - )); - assert_noop!( - TemplateModule::reveal_vote_score_helper( - key.clone(), - 15, - 8, - "salt5".as_bytes().to_vec() - ), - Error::::CommitDoesNotMatch - ); - assert_ok!(TemplateModule::reveal_vote_score_helper( - key.clone(), - 15, - 7, - "salt5".as_bytes().to_vec() - )); - let vote_start_time = TemplateModule::vote_start_time(key.clone()); - let new_now = vote_start_time + phase_data.commit_length; - assert_ok!(TemplateModule::change_period(key.clone(), phase_data.clone(), new_now.clone())); - let period = TemplateModule::get_period(key.clone()); - assert_eq!(Some(Period::Execution), period); - let reveal_score = TemplateModule::reveal_score_values(key.clone()); - assert_eq!(vec![1000, 1000, 5000, 1000, 7000], reveal_score); - let balance = Balances::free_balance(4); - assert_eq!(299600, balance); - let balance = Balances::free_balance(7); - // println!("{:?}", balance); - assert_eq!(299300, balance); - let balance = Balances::free_balance(13); - assert_eq!(298700, balance); - let balance = Balances::free_balance(14); - assert_eq!(298600, balance); - let balance = Balances::free_balance(15); - assert_eq!(298500, balance); - assert_ok!(TemplateModule::get_incentives_score_schelling_helper( - key.clone(), - phase_data.clone(), - RangePoint::ZeroToTen - )); - let mean_values = TemplateModule::new_mean_reveal_score(key.clone()); - assert_eq!(2000, mean_values); - let balance = Balances::free_balance(4); - // println!("{:?}", balance); - assert_eq!(300033, balance); - let balance = Balances::free_balance(7); - assert_eq!(300033, balance); - let balance = Balances::free_balance(13); // Balance deducted as voted 5 - assert_eq!(299675, balance); - let balance = Balances::free_balance(14); - assert_eq!(300033, balance); - let balance = Balances::free_balance(15); // Balance deducted as voted 7 - assert_eq!(299625, balance); - }); -} diff --git a/pallets/services-payment/Cargo.toml b/pallets/services-payment/Cargo.toml new file mode 100644 index 0000000..651a3fa --- /dev/null +++ b/pallets/services-payment/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "pallet-services-payment" +authors = [] +description = "Services payment pallet" +edition = "2021" +publish = false +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +cumulus-primitives-core = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive", "max-encoded-len" ] } +scale-info = { workspace = true } +serde = { workspace = true, default-features = false, features = [ "derive" ] } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +tp-traits = { workspace = true } + +[dev-dependencies] +pallet-balances = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "cumulus-primitives-core/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances/std", + "parity-scale-codec/std", + "scale-info/std", + "serde/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/services-payment/rpc/runtime-api/Cargo.toml b/pallets/services-payment/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000..ab0042e --- /dev/null +++ b/pallets/services-payment/rpc/runtime-api/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "pallet-services-payment-runtime-api" +authors = { workspace = true } +description = "Runtime API definition of pallet-services-payment" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +parity-scale-codec = { workspace = true } +sp-api = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "parity-scale-codec/std", + "sp-api/std", +] diff --git a/pallets/services-payment/rpc/runtime-api/src/lib.rs b/pallets/services-payment/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000..ce98778 --- /dev/null +++ b/pallets/services-payment/rpc/runtime-api/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Runtime API for Services Payment pallet + +#![cfg_attr(not(feature = "std"), no_std)] + +sp_api::decl_runtime_apis! { + pub trait ServicesPaymentApi + where + Balance: parity_scale_codec::Codec, + ParaId: parity_scale_codec::Codec, + { + fn block_cost(para_id: ParaId) -> Balance; + fn collator_assignment_cost(para_id: ParaId) -> Balance; + } +} diff --git a/pallets/services-payment/src/benchmarks.rs b/pallets/services-payment/src/benchmarks.rs new file mode 100644 index 0000000..6efe592 --- /dev/null +++ b/pallets/services-payment/src/benchmarks.rs @@ -0,0 +1,233 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#![cfg(feature = "runtime-benchmarks")] + +//! Benchmarking +use { + crate::{ + BalanceOf, BlockNumberFor, Call, Config, Pallet, ProvideBlockProductionCost, + ProvideCollatorAssignmentCost, + }, + frame_benchmarking::{account, v2::*}, + frame_support::{ + assert_ok, + traits::{Currency, EnsureOriginWithArg, Get}, + }, + frame_system::RawOrigin, + sp_runtime::Saturating, + sp_std::prelude::*, + tp_traits::{AuthorNotingHook, CollatorAssignmentHook}, +}; + +// Build genesis storage according to the mock runtime. +#[cfg(test)] +pub fn new_test_ext() -> sp_io::TestExternalities { + const ALICE: u64 = 1; + + crate::mock::ExtBuilder::default() + .with_balances(vec![(ALICE, 1_000)]) + .build() +} + +const SEED: u32 = 0; + +fn create_funded_user( + string: &'static str, + n: u32, + balance_factor: u32, +) -> T::AccountId { + let user = account(string, n, SEED); + let balance = ::minimum_balance() * balance_factor.into(); + let _ = ::make_free_balance_be(&user, balance); + user +} + +#[benchmarks(where BalanceOf: From>)] +mod benchmarks { + use super::*; + + #[benchmark] + fn purchase_credits() { + let para_id = 1001u32.into(); + let payment: BalanceOf = T::ProvideBlockProductionCost::block_cost(¶_id) + .0 + .saturating_mul(1000u32.into()); + let caller = create_funded_user::("caller", 1, 1_000_000_000u32); + + // Before call: 0 credits + assert_eq!( + crate::BlockProductionCredits::::get(para_id).unwrap_or_default(), + 0u32.into() + ); + + #[extrinsic_call] + Pallet::::purchase_credits(RawOrigin::Signed(caller), para_id, payment); + + // verification code + assert_eq!( + ::total_balance(&crate::Pallet::::parachain_tank(para_id)), + payment + ); + } + + #[benchmark] + fn set_block_production_credits() { + let para_id = 1001u32.into(); + let credits = T::FreeBlockProductionCredits::get(); + + assert_ok!(Pallet::::set_block_production_credits( + RawOrigin::Root.into(), + para_id, + credits, + )); + + // Before call: 1000 credits + assert_eq!( + crate::BlockProductionCredits::::get(para_id).unwrap_or_default(), + T::FreeBlockProductionCredits::get() + ); + + #[extrinsic_call] + Pallet::::set_block_production_credits(RawOrigin::Root, para_id, 1u32.into()); + + // After call: 1 credit + assert_eq!( + crate::BlockProductionCredits::::get(para_id).unwrap_or_default(), + 1u32.into() + ); + } + + #[benchmark] + fn set_given_free_credits() { + let para_id = 1001u32.into(); + + // Before call: no given free credits + assert!(crate::GivenFreeCredits::::get(para_id).is_none()); + + #[extrinsic_call] + Pallet::::set_given_free_credits(RawOrigin::Root, para_id, true); + + // After call: given free credits + assert!(crate::GivenFreeCredits::::get(para_id).is_some()); + } + + #[benchmark] + fn set_refund_address() { + let para_id = 1001u32.into(); + + let origin = T::ManagerOrigin::try_successful_origin(¶_id) + .expect("failed to create ManagerOrigin"); + + let refund_address = account("sufficient", 0, 1000); + + // Before call: no given free credits + assert!(crate::RefundAddress::::get(para_id).is_none()); + + #[extrinsic_call] + Pallet::::set_refund_address(origin as T::RuntimeOrigin, para_id, Some(refund_address)); + + // After call: given free credits + assert!(crate::RefundAddress::::get(para_id).is_some()); + } + + #[benchmark] + fn set_max_core_price() { + let para_id = 1001u32.into(); + + let origin = T::ManagerOrigin::try_successful_origin(¶_id) + .expect("failed to create ManagerOrigin"); + + let max_price = 100_000_000; + + // Before call: none + assert_eq!(crate::MaxCorePrice::::get(para_id), None); + + #[extrinsic_call] + Pallet::::set_max_core_price(origin as T::RuntimeOrigin, para_id, Some(max_price)); + + // After call: some + assert_eq!(crate::MaxCorePrice::::get(para_id), Some(max_price)); + } + + #[benchmark] + fn on_container_author_noted() { + let para_id = 1001u32; + let block_cost = T::ProvideBlockProductionCost::block_cost(¶_id.into()).0; + let credits: BalanceOf = 1000u32.into(); + let balance_to_purchase = block_cost.saturating_mul(credits); + let caller = create_funded_user::("caller", 1, 1_000_000_000u32); + let existential_deposit = ::minimum_balance(); + assert_ok!(Pallet::::purchase_credits( + RawOrigin::Signed(caller.clone()).into(), + para_id.into(), + balance_to_purchase + existential_deposit + )); + #[block] + { + as AuthorNotingHook>::on_container_author_noted( + &caller, + 0, + para_id.into(), + ); + } + } + + #[benchmark] + fn on_collators_assigned() { + let para_id = 1001u32; + let collator_assignment_cost = + T::ProvideCollatorAssignmentCost::collator_assignment_cost(¶_id.into()).0; + let max_credit_stored = T::FreeCollatorAssignmentCredits::get(); + let balance_to_purchase = collator_assignment_cost.saturating_mul(max_credit_stored.into()); + let caller = create_funded_user::("caller", 1, 1_000_000_000u32); + let existential_deposit = ::minimum_balance(); + let tip = 1_000_000u32; + assert_ok!(Pallet::::purchase_credits( + RawOrigin::Signed(caller.clone()).into(), + para_id.into(), + balance_to_purchase + existential_deposit + tip.into() + )); + assert_ok!(Pallet::::set_max_tip( + RawOrigin::Root.into(), + para_id.into(), + Some(tip.into()) + )); + #[block] + { + as CollatorAssignmentHook>>::on_collators_assigned( + para_id.into(), + Some(&tip.into()), + false, + ) + .expect("failed on_collators_assigned"); + } + } + + #[benchmark] + fn set_max_tip() { + let para_id = 1001u32.into(); + + assert!(crate::MaxTip::::get(para_id).is_none()); + + #[extrinsic_call] + Pallet::::set_max_tip(RawOrigin::Root, para_id, Some(1_000_000u32.into())); + + assert!(crate::MaxTip::::get(para_id).is_some()); + } + + impl_benchmark_test_suite!(Pallet, crate::benchmarks::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/services-payment/src/lib.rs b/pallets/services-payment/src/lib.rs new file mode 100644 index 0000000..0b372e8 --- /dev/null +++ b/pallets/services-payment/src/lib.rs @@ -0,0 +1,640 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! # Services Payment pallet +//! +//! This pallet allows for block creation services to be paid for by a +//! containerChain. + +#![cfg_attr(not(feature = "std"), no_std)] + +use { + cumulus_primitives_core::ParaId, + frame_support::{ + pallet_prelude::*, + sp_runtime::{traits::Zero, Saturating}, + traits::{ + tokens::ExistenceRequirement, Currency, EnsureOriginWithArg, OnUnbalanced, + WithdrawReasons, + }, + }, + frame_system::pallet_prelude::*, + scale_info::prelude::vec::Vec, + serde::{Deserialize, Serialize}, + sp_io::hashing::blake2_256, + sp_runtime::{traits::TrailingZeroInput, DispatchError}, + tp_traits::{AuthorNotingHook, BlockNumber, CollatorAssignmentHook, CollatorAssignmentTip}, +}; + +#[cfg(any(test, feature = "runtime-benchmarks"))] +mod benchmarks; +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; +pub mod weights; +pub use weights::WeightInfo; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Handlers for fees + type OnChargeForBlock: OnUnbalanced>; + type OnChargeForCollatorAssignment: OnUnbalanced>; + type OnChargeForCollatorAssignmentTip: OnUnbalanced>; + + /// Currency type for fee payment + type Currency: Currency; + /// Provider of a block cost which can adjust from block to block + type ProvideBlockProductionCost: ProvideBlockProductionCost; + /// Provider of a block cost which can adjust from block to block + type ProvideCollatorAssignmentCost: ProvideCollatorAssignmentCost; + + /// The maximum number of block production credits that can be accumulated + #[pallet::constant] + type FreeBlockProductionCredits: Get>; + + /// The maximum number of collator assigment production credits that can be accumulated + #[pallet::constant] + type FreeCollatorAssignmentCredits: Get; + /// Owner of the container chain, can call some only-owner methods + type ManagerOrigin: EnsureOriginWithArg; + + type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + InsufficientFundsToPurchaseCredits, + InsufficientCredits, + CreditPriceTooExpensive, + } + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + CreditsPurchased { + para_id: ParaId, + payer: T::AccountId, + credit: BalanceOf, + }, + BlockProductionCreditBurned { + para_id: ParaId, + credits_remaining: BlockNumberFor, + }, + CollatorAssignmentCreditBurned { + para_id: ParaId, + credits_remaining: u32, + }, + CollatorAssignmentTipCollected { + para_id: ParaId, + payer: T::AccountId, + tip: BalanceOf, + }, + BlockProductionCreditsSet { + para_id: ParaId, + credits: BlockNumberFor, + }, + RefundAddressUpdated { + para_id: ParaId, + refund_address: Option, + }, + MaxCorePriceUpdated { + para_id: ParaId, + max_core_price: Option, + }, + CollatorAssignmentCreditsSet { + para_id: ParaId, + credits: u32, + }, + } + + #[pallet::storage] + #[pallet::getter(fn free_block_production_credits)] + pub type BlockProductionCredits = + StorageMap<_, Blake2_128Concat, ParaId, BlockNumberFor, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn free_collator_assignment_credits)] + pub type CollatorAssignmentCredits = + StorageMap<_, Blake2_128Concat, ParaId, u32, OptionQuery>; + + /// List of para ids that have already been given free credits + #[pallet::storage] + #[pallet::getter(fn given_free_credits)] + pub type GivenFreeCredits = StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>; + + /// Refund address + #[pallet::storage] + #[pallet::getter(fn refund_address)] + pub type RefundAddress = + StorageMap<_, Blake2_128Concat, ParaId, T::AccountId, OptionQuery>; + + /// Max core price for parathread in relay chain currency + #[pallet::storage] + pub type MaxCorePrice = StorageMap<_, Blake2_128Concat, ParaId, u128, OptionQuery>; + + /// Max tip for collator assignment on congestion + #[pallet::storage] + #[pallet::getter(fn max_tip)] + pub type MaxTip = StorageMap<_, Blake2_128Concat, ParaId, BalanceOf, OptionQuery>; + + #[pallet::call] + impl Pallet + where + BlockNumberFor: Into>, + { + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::purchase_credits())] + pub fn purchase_credits( + origin: OriginFor, + para_id: ParaId, + credit: BalanceOf, + ) -> DispatchResultWithPostInfo { + let account = ensure_signed(origin)?; + let parachain_tank = Self::parachain_tank(para_id); + T::Currency::transfer( + &account, + ¶chain_tank, + credit, + ExistenceRequirement::KeepAlive, + )?; + + Self::deposit_event(Event::::CreditsPurchased { + para_id, + payer: account, + credit, + }); + + Ok(().into()) + } + + /// Set the number of block production credits for this para_id without paying for them. + /// Can only be called by root. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::set_block_production_credits())] + pub fn set_block_production_credits( + origin: OriginFor, + para_id: ParaId, + free_block_credits: BlockNumberFor, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + Self::set_free_block_production_credits(¶_id, free_block_credits); + + Ok(().into()) + } + + /// Helper to set and cleanup the `GivenFreeCredits` storage. + /// Can only be called by root. + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::set_given_free_credits())] + pub fn set_given_free_credits( + origin: OriginFor, + para_id: ParaId, + given_free_credits: bool, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + if given_free_credits { + GivenFreeCredits::::insert(para_id, ()); + } else { + GivenFreeCredits::::remove(para_id); + } + + Ok(().into()) + } + + /// Call index to set the refund address for non-spent tokens + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::set_refund_address())] + pub fn set_refund_address( + origin: OriginFor, + para_id: ParaId, + refund_address: Option, + ) -> DispatchResultWithPostInfo { + T::ManagerOrigin::ensure_origin(origin, ¶_id)?; + + if let Some(refund_address) = refund_address.clone() { + RefundAddress::::insert(para_id, refund_address.clone()); + } else { + RefundAddress::::remove(para_id); + } + + Self::deposit_event(Event::::RefundAddressUpdated { + para_id, + refund_address, + }); + + Ok(().into()) + } + + /// Set the number of block production credits for this para_id without paying for them. + /// Can only be called by root. + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::set_block_production_credits())] + pub fn set_collator_assignment_credits( + origin: OriginFor, + para_id: ParaId, + free_collator_assignment_credits: u32, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + Self::set_free_collator_assignment_credits(¶_id, free_collator_assignment_credits); + + Ok(().into()) + } + + /// Max core price for parathread in relay chain currency + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::set_max_core_price())] + pub fn set_max_core_price( + origin: OriginFor, + para_id: ParaId, + max_core_price: Option, + ) -> DispatchResultWithPostInfo { + T::ManagerOrigin::ensure_origin(origin, ¶_id)?; + + if let Some(max_core_price) = max_core_price { + MaxCorePrice::::insert(para_id, max_core_price); + } else { + MaxCorePrice::::remove(para_id); + } + + Self::deposit_event(Event::::MaxCorePriceUpdated { + para_id, + max_core_price, + }); + + Ok(().into()) + } + + /// Set the maximum tip a container chain is willing to pay to be assigned a collator on congestion. + /// Can only be called by container chain manager. + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::set_max_tip())] + pub fn set_max_tip( + origin: OriginFor, + para_id: ParaId, + max_tip: Option>, + ) -> DispatchResultWithPostInfo { + T::ManagerOrigin::ensure_origin(origin, ¶_id)?; + + if let Some(max_tip) = max_tip { + MaxTip::::insert(para_id, max_tip); + } else { + MaxTip::::remove(para_id); + } + + Ok(().into()) + } + } + + impl Pallet { + /// Burn a credit for the given para. Deducts one credit if possible, errors otherwise. + pub fn burn_block_production_free_credit_for_para( + para_id: &ParaId, + ) -> DispatchResultWithPostInfo { + let existing_credits = + BlockProductionCredits::::get(para_id).unwrap_or(BlockNumberFor::::zero()); + + ensure!( + existing_credits >= 1u32.into(), + Error::::InsufficientCredits, + ); + + let updated_credits = existing_credits.saturating_sub(1u32.into()); + BlockProductionCredits::::insert(para_id, updated_credits); + + Self::deposit_event(Event::::BlockProductionCreditBurned { + para_id: *para_id, + credits_remaining: updated_credits, + }); + + Ok(().into()) + } + + /// Burn a credit for the given para. Deducts one credit if possible, errors otherwise. + pub fn burn_collator_assignment_free_credit_for_para( + para_id: &ParaId, + ) -> DispatchResultWithPostInfo { + let existing_credits = CollatorAssignmentCredits::::get(para_id).unwrap_or(0u32); + + ensure!(existing_credits >= 1u32, Error::::InsufficientCredits,); + + let updated_credits = existing_credits.saturating_sub(1u32); + CollatorAssignmentCredits::::insert(para_id, updated_credits); + + Self::deposit_event(Event::::CollatorAssignmentCreditBurned { + para_id: *para_id, + credits_remaining: updated_credits, + }); + + Ok(().into()) + } + + pub fn give_free_credits(para_id: &ParaId) -> Weight { + if GivenFreeCredits::::contains_key(para_id) { + // This para id has already received free credits + return Weight::default(); + } + + // Set number of credits to FreeBlockProductionCredits + let block_production_existing_credits = + BlockProductionCredits::::get(para_id).unwrap_or(BlockNumberFor::::zero()); + let block_production_updated_credits = T::FreeBlockProductionCredits::get(); + // Do not update credits if for some reason this para id had more + if block_production_existing_credits < block_production_updated_credits { + Self::set_free_block_production_credits(para_id, block_production_updated_credits); + } + + // Set number of credits to FreeCollatorAssignmentCredits + let collator_assignment_existing_credits = + CollatorAssignmentCredits::::get(para_id).unwrap_or(0u32); + let collator_assignment_updated_credits = T::FreeCollatorAssignmentCredits::get(); + + // Do not update credits if for some reason this para id had more + if collator_assignment_existing_credits < collator_assignment_updated_credits { + Self::set_free_collator_assignment_credits( + para_id, + collator_assignment_updated_credits, + ); + } + + // We only allow to call this function once per para id, even if it didn't actually + // receive all the free credits + GivenFreeCredits::::insert(para_id, ()); + + Weight::default() + } + + pub fn set_free_collator_assignment_credits( + para_id: &ParaId, + free_collator_assignment_credits: u32, + ) { + if free_collator_assignment_credits.is_zero() { + CollatorAssignmentCredits::::remove(para_id); + } else { + CollatorAssignmentCredits::::insert(para_id, free_collator_assignment_credits); + } + + Self::deposit_event(Event::::CollatorAssignmentCreditsSet { + para_id: *para_id, + credits: free_collator_assignment_credits, + }); + } + + pub fn set_free_block_production_credits( + para_id: &ParaId, + free_collator_block_production_credits: BlockNumberFor, + ) { + if free_collator_block_production_credits.is_zero() { + BlockProductionCredits::::remove(para_id); + } else { + BlockProductionCredits::::insert( + para_id, + free_collator_block_production_credits, + ); + } + + Self::deposit_event(Event::::BlockProductionCreditsSet { + para_id: *para_id, + credits: free_collator_block_production_credits, + }); + } + } + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub para_id_credits: Vec>>, + } + + impl Default for GenesisConfig { + fn default() -> Self { + Self { + para_id_credits: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + for para_id_credits in &self.para_id_credits { + BlockProductionCredits::::insert( + para_id_credits.para_id, + para_id_credits.block_production_credits, + ); + CollatorAssignmentCredits::::insert( + para_id_credits.para_id, + para_id_credits.collator_assignment_credits, + ); + } + } + } +} + +// Params to be set in genesis +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Serialize, Deserialize)] +pub struct FreeCreditGenesisParams { + pub para_id: ParaId, + pub block_production_credits: BlockProductCredits, + pub collator_assignment_credits: u32, +} +impl From<(ParaId, BlockProductCredits, u32)> + for FreeCreditGenesisParams +{ + fn from(value: (ParaId, BlockProductCredits, u32)) -> Self { + Self { + para_id: value.0, + block_production_credits: value.1, + collator_assignment_credits: value.2, + } + } +} + +/// Balance used by this pallet +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +pub type CurrencyOf = ::Currency; +/// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type. +pub type NegativeImbalanceOf = + as Currency<::AccountId>>::NegativeImbalance; +/// Handler for fee charging. This will be invoked when fees need to be deducted from the fee +/// account for a given paraId. + +/// Returns the cost for a given block credit at the current time. This can be a complex operation, +/// so it also returns the weight it consumes. (TODO: or just rely on benchmarking) +pub trait ProvideBlockProductionCost { + fn block_cost(para_id: &ParaId) -> (BalanceOf, Weight); +} + +/// Returns the cost for a given block credit at the current time. This can be a complex operation, +/// so it also returns the weight it consumes. (TODO: or just rely on benchmarking) +pub trait ProvideCollatorAssignmentCost { + fn collator_assignment_cost(para_id: &ParaId) -> (BalanceOf, Weight); +} + +impl AuthorNotingHook for Pallet { + // This hook is called when pallet_author_noting sees that the block number of a container chain has increased. + // Currently we always charge 1 credit, even if a container chain produced more that 1 block in between tanssi + // blocks. + fn on_container_author_noted( + _author: &T::AccountId, + _block_number: BlockNumber, + para_id: ParaId, + ) -> Weight { + if Pallet::::burn_block_production_free_credit_for_para(¶_id).is_err() { + let (amount_to_charge, _weight) = T::ProvideBlockProductionCost::block_cost(¶_id); + match T::Currency::withdraw( + &Self::parachain_tank(para_id), + amount_to_charge, + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + ) { + Err(e) => log::warn!( + "Failed to withdraw block production payment for container chain {}: {:?}", + u32::from(para_id), + e + ), + Ok(imbalance) => { + T::OnChargeForBlock::on_unbalanced(imbalance); + } + } + } + + T::WeightInfo::on_container_author_noted() + } +} + +impl CollatorAssignmentHook> for Pallet { + // is_parathread parameter for future use to apply different logic + fn on_collators_assigned( + para_id: ParaId, + maybe_tip: Option<&BalanceOf>, + _is_parathread: bool, + ) -> Result { + // Withdraw assignment fee + let maybe_assignment_imbalance = + if Pallet::::burn_collator_assignment_free_credit_for_para(¶_id).is_err() { + let (amount_to_charge, _weight) = + T::ProvideCollatorAssignmentCost::collator_assignment_cost(¶_id); + Some(T::Currency::withdraw( + &Self::parachain_tank(para_id), + amount_to_charge, + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + )?) + } else { + None + }; + + if let Some(&tip) = maybe_tip { + // Only charge the tip to the paras that had a max tip set + // (aka were willing to tip for being assigned a collator) + if MaxTip::::get(para_id).is_some() { + match T::Currency::withdraw( + &Self::parachain_tank(para_id), + tip, + WithdrawReasons::TIP, + ExistenceRequirement::KeepAlive, + ) { + Err(e) => { + // Return assignment imbalance to tank on error + if let Some(assignment_imbalance) = maybe_assignment_imbalance { + T::Currency::resolve_creating( + &Self::parachain_tank(para_id), + assignment_imbalance, + ); + } + return Err(e); + } + Ok(tip_imbalance) => { + Self::deposit_event(Event::::CollatorAssignmentTipCollected { + para_id, + payer: Self::parachain_tank(para_id), + tip, + }); + T::OnChargeForCollatorAssignmentTip::on_unbalanced(tip_imbalance); + } + } + } + } + + if let Some(assignment_imbalance) = maybe_assignment_imbalance { + T::OnChargeForCollatorAssignment::on_unbalanced(assignment_imbalance); + } + + Ok(T::WeightInfo::on_collators_assigned()) + } +} + +impl CollatorAssignmentTip> for Pallet { + fn get_para_tip(para_id: ParaId) -> Option> { + MaxTip::::get(para_id) + } +} + +impl Pallet { + /// Derive a derivative account ID from the paraId. + pub fn parachain_tank(para_id: ParaId) -> T::AccountId { + let entropy = (b"modlpy/serpayment", para_id).using_encoded(blake2_256); + Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .expect("infinite length input; no invalid inputs for type; qed") + } + + /// Hook to perform things on deregister + pub fn para_deregistered(para_id: ParaId) { + // Drain the para-id account from tokens + let parachain_tank_balance = T::Currency::total_balance(&Self::parachain_tank(para_id)); + if !parachain_tank_balance.is_zero() { + if let Ok(imbalance) = T::Currency::withdraw( + &Self::parachain_tank(para_id), + parachain_tank_balance, + WithdrawReasons::FEE, + ExistenceRequirement::AllowDeath, + ) { + if let Some(address) = RefundAddress::::get(para_id) { + T::Currency::resolve_creating(&address, imbalance); + } else { + // Burn for now, we might be able to pass something to do with this + drop(imbalance); + } + } + } + + // Clean refund addres + RefundAddress::::remove(para_id); + + // Clean credits + BlockProductionCredits::::remove(para_id); + CollatorAssignmentCredits::::remove(para_id); + MaxTip::::remove(para_id); + MaxCorePrice::::remove(para_id); + } +} diff --git a/pallets/services-payment/src/mock.rs b/pallets/services-payment/src/mock.rs new file mode 100644 index 0000000..120f769 --- /dev/null +++ b/pallets/services-payment/src/mock.rs @@ -0,0 +1,170 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + self as pallet_services_payment, ProvideBlockProductionCost, ProvideCollatorAssignmentCost, + }, + cumulus_primitives_core::ParaId, + frame_support::{ + pallet_prelude::*, + parameter_types, + traits::{ConstU32, ConstU64, Everything}, + }, + frame_system::EnsureRoot, + sp_core::H256, + sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, + }, +}; + +type Block = frame_system::mocking::MockBlock; +type AccountId = u64; +type Balance = u128; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + PaymentServices: pallet_services_payment::{Pallet, Call, Config, Storage, Event} + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; + type RuntimeTask = (); +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} + +impl pallet_balances::Config for Test { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); + type MaxHolds = (); + type WeightInfo = (); +} + +parameter_types! { + pub const FreeBlockProductionCredits: u64 = 5; + pub const FreeCollatorAssignmentCredits: u32 = 5; +} + +impl pallet_services_payment::Config for Test { + type RuntimeEvent = RuntimeEvent; + type OnChargeForBlock = (); + type OnChargeForCollatorAssignment = (); + type OnChargeForCollatorAssignmentTip = (); + type Currency = Balances; + type ProvideBlockProductionCost = BlockProductionCost; + type ProvideCollatorAssignmentCost = CollatorAssignmentProductionCost; + type FreeBlockProductionCredits = FreeBlockProductionCredits; + type FreeCollatorAssignmentCredits = FreeCollatorAssignmentCredits; + type ManagerOrigin = EnsureRoot; + type WeightInfo = (); +} + +pub(crate) const FIXED_BLOCK_PRODUCTION_COST: u128 = 100; +pub(crate) const FIXED_COLLATOR_ASSIGNMENT_COST: u128 = 200; + +pub struct BlockProductionCost(PhantomData); +impl ProvideBlockProductionCost for BlockProductionCost { + fn block_cost(_para_id: &ParaId) -> (u128, Weight) { + (FIXED_BLOCK_PRODUCTION_COST, Weight::zero()) + } +} + +pub struct CollatorAssignmentProductionCost(PhantomData); +impl ProvideCollatorAssignmentCost for CollatorAssignmentProductionCost { + fn collator_assignment_cost(_para_id: &ParaId) -> (u128, Weight) { + (FIXED_COLLATOR_ASSIGNMENT_COST, Weight::zero()) + } +} + +#[derive(Default)] +pub struct ExtBuilder { + balances: Vec<(AccountId, Balance)>, +} + +impl ExtBuilder { + pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: self.balances, + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() + } +} + +pub(crate) fn events() -> Vec> { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let RuntimeEvent::PaymentServices(inner) = e { + Some(inner) + } else { + None + } + }) + .collect::>() +} diff --git a/pallets/services-payment/src/tests.rs b/pallets/services-payment/src/tests.rs new file mode 100644 index 0000000..7f6f2ee --- /dev/null +++ b/pallets/services-payment/src/tests.rs @@ -0,0 +1,509 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + mock::*, pallet as pallet_services_payment, BlockProductionCredits, + CollatorAssignmentCredits, ProvideBlockProductionCost, ProvideCollatorAssignmentCost, + RefundAddress, + }, + cumulus_primitives_core::ParaId, + frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Inspect}, + sp_runtime::DispatchError, + tp_traits::{AuthorNotingHook, CollatorAssignmentHook}, +}; + +const ALICE: u64 = 1; + +#[test] +fn purchase_credits_works() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + System::set_block_number(1); + + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 100u128, + ),); + + assert_eq!( + events(), + vec![pallet_services_payment::Event::CreditsPurchased { + para_id: 1.into(), + payer: ALICE, + credit: 100u128 + }] + ); + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 100u128 + ); + }); +} +#[test] +fn purchase_credits_fails_with_insufficient_balance() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + // cannot purchase if death + assert_err!( + PaymentServices::purchase_credits(RuntimeOrigin::signed(ALICE), 1.into(), 1000u128), + sp_runtime::TokenError::NotExpendable, + ); + }); +} + +#[test] +fn burn_credit_fails_with_no_credits() { + ExtBuilder::default().build().execute_with(|| { + assert_err!( + PaymentServices::burn_block_production_free_credit_for_para(&1u32.into()), + pallet_services_payment::Error::::InsufficientCredits, + ); + assert_err!( + PaymentServices::burn_collator_assignment_free_credit_for_para(&1u32.into()), + pallet_services_payment::Error::::InsufficientCredits, + ); + }); +} + +#[test] +fn burn_block_production_credit_works() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + let para_id = 1.into(); + assert_ok!(PaymentServices::set_block_production_credits( + RuntimeOrigin::root(), + para_id, + 1u64, + ),); + + // should succeed and burn one + assert_eq!(>::get(para_id), Some(1u64)); + assert_ok!(PaymentServices::burn_block_production_free_credit_for_para( + ¶_id + )); + assert_eq!(>::get(para_id), Some(0u64)); + + // now should fail + assert_err!( + PaymentServices::burn_block_production_free_credit_for_para(¶_id), + pallet_services_payment::Error::::InsufficientCredits, + ); + }); +} + +#[test] +fn burn_collator_assignment_credit_works() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + let para_id = 1.into(); + assert_ok!(PaymentServices::set_collator_assignment_credits( + RuntimeOrigin::root(), + para_id, + 1u32, + ),); + + // should succeed and burn one + assert_eq!(>::get(para_id), Some(1u32)); + assert_ok!(PaymentServices::burn_collator_assignment_free_credit_for_para(¶_id)); + assert_eq!(>::get(para_id), Some(0u32)); + + // now should fail + assert_err!( + PaymentServices::burn_collator_assignment_free_credit_for_para(¶_id), + pallet_services_payment::Error::::InsufficientCredits, + ); + }); +} + +#[test] +fn burn_credit_fails_for_wrong_para() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + let para_id = 1.into(); + assert_ok!(PaymentServices::set_block_production_credits( + RuntimeOrigin::root(), + para_id, + 1u64, + ),); + assert_ok!(PaymentServices::set_collator_assignment_credits( + RuntimeOrigin::root(), + para_id, + 1u32, + ),); + + // fails for wrong para + let wrong_para_id = 2.into(); + assert_err!( + PaymentServices::burn_block_production_free_credit_for_para(&wrong_para_id), + pallet_services_payment::Error::::InsufficientCredits, + ); + assert_err!( + PaymentServices::burn_collator_assignment_free_credit_for_para(&wrong_para_id), + pallet_services_payment::Error::::InsufficientCredits, + ); + }); +} + +#[test] +fn set_block_production_credits_bad_origin() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + assert_err!( + PaymentServices::set_block_production_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 1u64, + ), + DispatchError::BadOrigin + ) + }); +} + +#[test] +fn set_block_production_credits_above_max_works() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + assert_ok!(PaymentServices::set_block_production_credits( + RuntimeOrigin::root(), + 1.into(), + FreeBlockProductionCredits::get() * 2, + )); + + assert_eq!( + >::get(ParaId::from(1)), + Some(FreeBlockProductionCredits::get() * 2) + ); + }); +} + +#[test] +fn set_block_production_credits_to_zero_kills_storage() { + ExtBuilder::default() + .with_balances([(ALICE, 1_000)].into()) + .build() + .execute_with(|| { + assert_ok!(PaymentServices::set_block_production_credits( + RuntimeOrigin::root(), + 1.into(), + 0u64, + )); + + assert_eq!(>::get(ParaId::from(1)), None,); + }); +} + +#[test] +fn credits_should_be_substracted_from_tank_if_no_free_credits() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000)].into()) + .build() + .execute_with(|| { + // this should give 10 block credit + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 1000u128, + )); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 1000u128 + ); + + PaymentServices::on_container_author_noted(&1, 1, 1.into()); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 900u128 + ); + }); +} + +#[test] +fn credits_should_not_be_substracted_from_tank_if_it_involves_death() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000)].into()) + .build() + .execute_with(|| { + // this should give 10 block credit + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 100u128, + )); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 100u128 + ); + + PaymentServices::on_container_author_noted(&1, 1, 1.into()); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 100u128 + ); + + assert_noop!( + PaymentServices::on_collators_assigned(1.into(), None, false), + pallet_balances::Error::::InsufficientBalance + ); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 100u128 + ); + }); +} + +#[test] +fn not_having_enough_tokens_in_tank_should_not_error() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000)].into()) + .build() + .execute_with(|| { + // this should give 10 block credit + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 1u128, + )); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 1u128 + ); + + PaymentServices::on_container_author_noted(&1, 1, 1.into()); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(1.into())), + 1u128 + ); + }); +} + +#[test] +fn on_deregister_burns_if_no_deposit_address() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000)].into()) + .build() + .execute_with(|| { + // this should give 10 block credit + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 1000u128, + )); + + let issuance_before = Balances::total_issuance(); + crate::Pallet::::para_deregistered(1.into()); + let issuance_after = Balances::total_issuance(); + assert_eq!(issuance_after, issuance_before - 1000u128); + + // Refund address gets cleared + assert!(>::get(ParaId::from(1)).is_none()); + }); +} + +#[test] +fn on_deregister_cleans_refund_address_even_when_purchases_have_not_being_made() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000)].into()) + .build() + .execute_with(|| { + let refund_address = 10u64; + + assert_ok!(PaymentServices::set_refund_address( + RuntimeOrigin::root(), + 1.into(), + Some(refund_address), + )); + + crate::Pallet::::para_deregistered(1.into()); + + // Refund address gets cleared + assert!(>::get(ParaId::from(1)).is_none()); + }); +} + +#[test] +fn on_deregister_deposits_if_refund_address() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000)].into()) + .build() + .execute_with(|| { + let refund_address = 10u64; + // this should give 10 block credit + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 1000u128, + )); + + // this should set refund address + assert_ok!(PaymentServices::set_refund_address( + RuntimeOrigin::root(), + 1.into(), + Some(refund_address), + )); + + let issuance_before = Balances::total_issuance(); + crate::Pallet::::para_deregistered(1.into()); + let issuance_after = Balances::total_issuance(); + assert_eq!(issuance_after, issuance_before); + + let balance_refund_address = Balances::balance(&refund_address); + assert_eq!(balance_refund_address, 1000u128); + + assert!(>::get(ParaId::from(1)).is_none()); + }); +} + +#[test] +fn set_refund_address_with_none_removes_storage() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000)].into()) + .build() + .execute_with(|| { + let refund_address = 10u64; + // this should give 10 block credit + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + 1.into(), + 1000u128, + )); + + // this should set refund address + assert_ok!(PaymentServices::set_refund_address( + RuntimeOrigin::root(), + 1.into(), + Some(refund_address), + )); + + assert!(>::get(ParaId::from(1)).is_some()); + + assert_ok!(PaymentServices::set_refund_address( + RuntimeOrigin::root(), + 1.into(), + None, + )); + + assert!(>::get(ParaId::from(1)).is_none()); + }); +} + +#[test] +fn tip_should_be_charged_on_collators_assignment() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000_000)].into()) + .build() + .execute_with(|| { + let para_id = 1; + let tip = 10u128; + let balance = 5000u128; + + // this should give 10 block credit + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + para_id.into(), + balance, + )); + + assert_ok!(PaymentServices::set_max_tip( + RuntimeOrigin::root(), + para_id.into(), + Some(tip), + )); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(para_id.into())), + balance, + ); + + assert_ok!(PaymentServices::on_collators_assigned( + para_id.into(), + Some(&tip), + false + )); + + PaymentServices::on_container_author_noted(&1, 1, para_id.into()); + + let (assignment_cost, _weight) = + ::ProvideCollatorAssignmentCost::collator_assignment_cost( + ¶_id.into(), + ); + let (block_cost, _weight) = + ::ProvideBlockProductionCost::block_cost(¶_id.into()); + + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(para_id.into())), + balance - assignment_cost - block_cost - tip, + ); + }); +} + +#[test] +fn insufficient_balance_for_tip_reimburses_fee_imbalance() { + ExtBuilder::default() + .with_balances([(ALICE, 2_000_000)].into()) + .build() + .execute_with(|| { + let para_id = 1; + let tip = 10u128; + // Just enough for one assignment but not for tip; + let balance = 205u128; + + assert_ok!(PaymentServices::purchase_credits( + RuntimeOrigin::signed(ALICE), + para_id.into(), + balance, + )); + + assert_ok!(PaymentServices::set_max_tip( + RuntimeOrigin::root(), + para_id.into(), + Some(tip), + )); + + // it should fail when trying to withdraw the tip + assert!( + PaymentServices::on_collators_assigned(para_id.into(), Some(&tip), false).is_err() + ); + + // Tank balance shouldn't have changed + assert_eq!( + Balances::balance(&crate::Pallet::::parachain_tank(para_id.into())), + balance, + ); + }); +} diff --git a/pallets/services-payment/src/weights.rs b/pallets/services-payment/src/weights.rs new file mode 100644 index 0000000..808e749 --- /dev/null +++ b/pallets/services-payment/src/weights.rs @@ -0,0 +1,259 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + + +//! Autogenerated weights for pallet_services_payment +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `benchmark-1`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/tanssi-node +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet_services_payment +// --extrinsic +// * +// --chain=dev +// --steps +// 50 +// --repeat +// 20 +// --template=./benchmarking/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// tmp/dancebox_weights/pallet_services_payment.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_services_payment. +pub trait WeightInfo { + fn purchase_credits() -> Weight; + fn set_block_production_credits() -> Weight; + fn set_given_free_credits() -> Weight; + fn set_refund_address() -> Weight; + fn set_max_core_price() -> Weight; + fn on_container_author_noted() -> Weight; + fn on_collators_assigned() -> Weight; + fn set_max_tip() -> Weight; +} + +/// Weights for pallet_services_payment using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn purchase_credits() -> Weight { + // Proof Size summary in bytes: + // Measured: `155` + // Estimated: `6196` + // Minimum execution time: 57_174_000 picoseconds. + Weight::from_parts(57_971_000, 6196) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ServicesPayment::BlockProductionCredits` (r:0 w:1) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + fn set_block_production_credits() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_445_000 picoseconds. + Weight::from_parts(8_713_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::GivenFreeCredits` (r:0 w:1) + /// Proof: `ServicesPayment::GivenFreeCredits` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + fn set_given_free_credits() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_842_000 picoseconds. + Weight::from_parts(5_122_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::RefundAddress` (r:0 w:1) + /// Proof: `ServicesPayment::RefundAddress` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn set_refund_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 16_357_000 picoseconds. + Weight::from_parts(16_871_000, 3660) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::MaxCorePrice` (r:0 w:1) + /// Proof: `ServicesPayment::MaxCorePrice` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_max_core_price() -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 8_773_000 picoseconds. + Weight::from_parts(9_211_000, 3660) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::BlockProductionCredits` (r:1 w:0) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn on_container_author_noted() -> Weight { + // Proof Size summary in bytes: + // Measured: `258` + // Estimated: `3593` + // Minimum execution time: 24_336_000 picoseconds. + Weight::from_parts(24_786_000, 3593) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::CollatorAssignmentCredits` (r:1 w:0) + /// Proof: `ServicesPayment::CollatorAssignmentCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn on_collators_assigned() -> Weight { + // Proof Size summary in bytes: + // Measured: `258` + // Estimated: `3593` + // Minimum execution time: 24_209_000 picoseconds. + Weight::from_parts(24_682_000, 3593) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::MaxTip` (r:0 w:1) + /// Proof: `ServicesPayment::MaxTip` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_max_tip() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_157_000 picoseconds. + Weight::from_parts(4_576_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn purchase_credits() -> Weight { + // Proof Size summary in bytes: + // Measured: `155` + // Estimated: `6196` + // Minimum execution time: 57_174_000 picoseconds. + Weight::from_parts(57_971_000, 6196) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `ServicesPayment::BlockProductionCredits` (r:0 w:1) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + fn set_block_production_credits() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_445_000 picoseconds. + Weight::from_parts(8_713_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::GivenFreeCredits` (r:0 w:1) + /// Proof: `ServicesPayment::GivenFreeCredits` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + fn set_given_free_credits() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_842_000 picoseconds. + Weight::from_parts(5_122_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::RefundAddress` (r:0 w:1) + /// Proof: `ServicesPayment::RefundAddress` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn set_refund_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 16_357_000 picoseconds. + Weight::from_parts(16_871_000, 3660) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Registrar::RegistrarDeposit` (r:1 w:0) + /// Proof: `Registrar::RegistrarDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ServicesPayment::MaxCorePrice` (r:0 w:1) + /// Proof: `ServicesPayment::MaxCorePrice` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_max_core_price() -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3660` + // Minimum execution time: 8_773_000 picoseconds. + Weight::from_parts(9_211_000, 3660) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::BlockProductionCredits` (r:1 w:0) + /// Proof: `ServicesPayment::BlockProductionCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn on_container_author_noted() -> Weight { + // Proof Size summary in bytes: + // Measured: `258` + // Estimated: `3593` + // Minimum execution time: 24_336_000 picoseconds. + Weight::from_parts(24_786_000, 3593) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::CollatorAssignmentCredits` (r:1 w:0) + /// Proof: `ServicesPayment::CollatorAssignmentCredits` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn on_collators_assigned() -> Weight { + // Proof Size summary in bytes: + // Measured: `258` + // Estimated: `3593` + // Minimum execution time: 24_209_000 picoseconds. + Weight::from_parts(24_682_000, 3593) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ServicesPayment::MaxTip` (r:0 w:1) + /// Proof: `ServicesPayment::MaxTip` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_max_tip() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_157_000 picoseconds. + Weight::from_parts(4_576_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/shared-storage/Cargo.toml b/pallets/shared-storage/Cargo.toml deleted file mode 100644 index d4b117c..0000000 --- a/pallets/shared-storage/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "shared-storage" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -shared-storage-link = {default-features=false, path="../../traits/shared-storage-link"} - - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/shared-storage/src/benchmarking.rs b/pallets/shared-storage/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/shared-storage/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/shared-storage/src/extras.rs b/pallets/shared-storage/src/extras.rs deleted file mode 100644 index 22292ce..0000000 --- a/pallets/shared-storage/src/extras.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::*; - -use shared_storage_link::SharedStorageLink; - -impl SharedStorageLink for Pallet { - type AccountId = AccountIdOf; - - fn check_citizen_is_approved_link(address: Self::AccountId) -> DispatchResult { - Self::check_citizen_is_approved(address) - } - fn get_approved_citizen_count_link() -> u64 { - Self::get_approved_citizen_count() - } - - fn set_positive_externality_link(address: Self::AccountId, score: i64) -> DispatchResult { - Self::set_positive_externality(address, score) - } -} - -impl Pallet { - pub(super) fn check_citizen_is_approved(address: T::AccountId) -> DispatchResult { - let members = ApprovedCitizenAddress::::get(); - - match members.binary_search(&address) { - Ok(_index) => Ok(()), - Err(_) => Err(Error::::CitizenNotApproved.into()), - } - } - - pub(super) fn get_approved_citizen_count() -> u64 { - let members = ApprovedCitizenAddress::::get(); - members.len() as u64 - } - - pub(super) fn set_positive_externality(address: T::AccountId, score: Score) -> DispatchResult { - PositiveExternalityScore::::insert(address, score); - Ok(()) - } -} diff --git a/pallets/shared-storage/src/lib.rs b/pallets/shared-storage/src/lib.rs deleted file mode 100644 index 6d0183a..0000000 --- a/pallets/shared-storage/src/lib.rs +++ /dev/null @@ -1,147 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; -mod extras; - -use frame_support::sp_std::prelude::*; -use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; - -type AccountIdOf = ::AccountId; -type Score = i64; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: frame_system::Config { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::storage] - #[pallet::getter(fn approved_citizen_address)] - pub type ApprovedCitizenAddress = StorageValue<_, Vec, ValueQuery>; // Its set, add element through binary_search - - #[pallet::storage] - #[pallet::getter(fn positive_externality_score)] - pub type PositiveExternalityScore = - StorageMap<_, Blake2_128Concat, T::AccountId, Score, ValueQuery>; - - // Keep winning representatives of department in shared storage - - #[pallet::genesis_config] - pub struct GenesisConfig { - pub approved_citizen_address: Vec, - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { approved_citizen_address: Default::default() } - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - >::put(self.approved_citizen_address.clone()); - } - } - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { something: u32, who: T::AccountId }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - CitizenNotApproved, - } - - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. - #[pallet::call] - impl Pallet { - /// An example dispatchable that takes a singles value as a parameter, writes the value to - /// storage and emits an event. This function must be dispatched by a signed extrinsic. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::do_something())] - pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - // This function will return an error if the extrinsic is not signed. - // https://docs.substrate.io/main-docs/build/origins/ - let who = ensure_signed(origin)?; - - // Update storage. - >::put(something); - - // Emit an event. - Self::deposit_event(Event::SomethingStored { something, who }); - // Return a successful DispatchResultWithPostInfo - Ok(()) - } - - /// An example dispatchable that may throw a custom error. - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::cause_error())] - pub fn cause_error(origin: OriginFor) -> DispatchResult { - let _who = ensure_signed(origin)?; - - // Read a value from storage. - match >::get() { - // Return an error if the value has not been set. - None => return Err(Error::::NoneValue.into()), - Some(old) => { - // Increment the value read from storage; will error in the event of overflow. - let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; - // Update the value in storage with the incremented result. - >::put(new); - Ok(()) - }, - } - } - } -} diff --git a/pallets/shared-storage/src/mock.rs b/pallets/shared-storage/src/mock.rs deleted file mode 100644 index b4d6905..0000000 --- a/pallets/shared-storage/src/mock.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate as pallet_template; -use frame_support::traits::{ConstU16, ConstU64}; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - TemplateModule: pallet_template, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() -} diff --git a/pallets/shared-storage/src/tests.rs b/pallets/shared-storage/src/tests.rs deleted file mode 100644 index 7c2b853..0000000 --- a/pallets/shared-storage/src/tests.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{mock::*, Error, Event}; -use frame_support::{assert_noop, assert_ok}; - -#[test] -fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - // Dispatch a signed extrinsic. - assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); - // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); - // Assert that the correct event was deposited - System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into()); - }); -} - -#[test] -fn correct_error_for_none_value() { - new_test_ext().execute_with(|| { - // Ensure the expected error is thrown when no value is present. - assert_noop!( - TemplateModule::cause_error(RuntimeOrigin::signed(1)), - Error::::NoneValue - ); - }); -} diff --git a/pallets/sortition-sum-game/Cargo.toml b/pallets/sortition-sum-game/Cargo.toml deleted file mode 100644 index cb75ba2..0000000 --- a/pallets/sortition-sum-game/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "sortition-sum-game" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sortition-sum-game-link = {default-features = false, path="../../traits/sortition-sum-game-link"} - - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/sortition-sum-game/src/benchmarking.rs b/pallets/sortition-sum-game/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/sortition-sum-game/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/sortition-sum-game/src/extras.rs b/pallets/sortition-sum-game/src/extras.rs deleted file mode 100644 index 358901a..0000000 --- a/pallets/sortition-sum-game/src/extras.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::*; - -impl SortitionSumGameLink for Pallet { - type SumTreeName = SumTreeNameType; - type AccountId = AccountIdOf; - fn create_tree_link(key: Self::SumTreeName, k: u64) -> DispatchResult { - Self::create_tree(key, k) - } - - fn set_link(key: Self::SumTreeName, value: u64, citizen_id: Self::AccountId) -> DispatchResult { - Self::set(key, value, citizen_id) - } - fn stake_of_link( - key: Self::SumTreeName, - citizen_id: Self::AccountId, - ) -> Result, DispatchError> { - Self::stake_of(key, citizen_id) - } - fn draw_link( - key: Self::SumTreeName, - draw_number: u64, - ) -> Result { - Self::draw(key, draw_number) - } - fn remove_tree_link(key: Self::SumTreeName) -> DispatchResult { - Self::remove_tree(key) - } -} - -impl Pallet { - // SortitionSumTree - pub fn create_tree(key: SumTreeNameType, k: u64) -> DispatchResult { - if k < 1 { - Err(Error::::KMustGreaterThanOne)? - } - let tree_option = >::get(&key); - match tree_option { - Some(_tree) => Err(Error::::TreeAlreadyExists)?, - None => { - let mut sum_tree = SortitionSumTree { - k, - stack: Vec::new(), - nodes: Vec::new(), - ids_to_node_indexes: BTreeMap::new(), - node_indexes_to_ids: BTreeMap::new(), - }; - - sum_tree.nodes.push(0); - - >::insert(&key, &sum_tree); - }, - } - Ok(()) - } - - pub fn set(key: SumTreeNameType, value: u64, citizen_id: AccountIdOf) -> DispatchResult { - let tree_option = >::get(&key); - - match tree_option { - None => Err(Error::::TreeDoesnotExist)?, - Some(mut tree) => match tree.ids_to_node_indexes.get(&citizen_id) { - Some(tree_index_data) => { - let tree_index = *tree_index_data; - if tree_index == 0 { - Self::if_tree_index_zero(value, citizen_id, tree, tree_index, key); - } else { - // Existing node - if value == 0 { - let value = tree.nodes[tree_index as usize]; - tree.nodes[tree_index as usize] = 0; - tree.stack.push(tree_index); - tree.ids_to_node_indexes.remove(&citizen_id); - tree.node_indexes_to_ids.remove(&tree_index); - - // UpdateParents 🟥 - Self::update_parents(tree, tree_index, false, value, key); - } else if value != tree.nodes[tree_index as usize] { - let plus_or_minus = tree.nodes[tree_index as usize] <= value; - let plus_or_minus_value = if plus_or_minus { - value - .checked_sub(tree.nodes[tree_index as usize]) - .ok_or("StorageOverflow")? - } else { - (tree.nodes[tree_index as usize]) - .checked_sub(value) - .ok_or("StorageOverflow")? - }; - tree.nodes[tree_index as usize] = value; - - // update parents 🟥 - Self::update_parents( - tree, - tree_index, - plus_or_minus, - plus_or_minus_value, - key, - ); - } - } - }, - - None => { - Self::if_tree_index_zero(value, citizen_id, tree, 0, key); - }, - }, - } - - Ok(()) - } - - fn update_parents( - mut tree: SortitionSumTree>, - tree_index: u64, - plus_or_minus: bool, - value: u64, - key: SumTreeNameType, - ) { - let mut parent_index = tree_index; - while parent_index != 0 { - parent_index = (parent_index - 1) / tree.k; - tree.nodes[parent_index as usize] = if plus_or_minus { - (tree.nodes[parent_index as usize]).checked_add(value).expect("StorageOverflow") - } else { - (tree.nodes[parent_index as usize]).checked_sub(value).expect("StorageOverflow") - }; - - >::insert(&key, &tree); - } - } - fn if_tree_index_zero( - value: u64, - citizen_id: AccountIdOf, - mut tree: SortitionSumTree>, - mut tree_index: u64, - key: SumTreeNameType, - ) { - // No existing node. - if value != 0 { - // Non zero value. - // Append. - // Add node. - if tree.stack.len() == 0 { - // No vacant spots. - // Get the index and append the value. - tree_index = tree.nodes.len() as u64; - tree.nodes.push(value); - - // println!("{}", tree_index); - - // Potentially append a new node and make the parent a sum node. - if tree_index != 1 && (tree_index - 1) % tree.k == 0 { - // Is first child. - let parent_index = tree_index / tree.k; - let parent_id = tree.node_indexes_to_ids.get(&parent_index).unwrap().clone(); - let new_index = tree_index + 1; - tree.nodes.push(*tree.nodes.get(parent_index as usize).unwrap()); - tree.node_indexes_to_ids.remove(&parent_index); - tree.ids_to_node_indexes.insert(parent_id.clone(), new_index); - tree.node_indexes_to_ids.insert(new_index, parent_id); - } - } else { - let tree_index = tree.stack.get(tree.stack.len() - 1); - tree.nodes[*tree_index.unwrap() as usize] = value; - tree.stack.pop(); - } - - tree.ids_to_node_indexes.insert(citizen_id.clone(), tree_index); - tree.node_indexes_to_ids.insert(tree_index, citizen_id); - - // update_parents 🟥 - - Self::update_parents(tree, tree_index, true, value, key); - } - } - - pub fn stake_of( - key: SumTreeNameType, - citizen_id: AccountIdOf, - ) -> Result, DispatchError> { - let tree_option = >::get(&key); - match tree_option { - None => Err(Error::::TreeDoesnotExist)?, - Some(tree) => { - let tree_index_data; - match tree.ids_to_node_indexes.get(&citizen_id) { - Some(v) => tree_index_data = v, - None => return Ok(None), - } - - let value: u64; - let tree_index = *tree_index_data; - if tree_index == 0 { - value = 0; - } else { - value = tree.nodes[tree_index as usize]; - } - Ok(Some(value)) - }, - } - } - - pub fn draw( - key: SumTreeNameType, - draw_number: u64, - ) -> Result, DispatchError> { - let tree_option = >::get(&key); - - match tree_option { - None => Err(Error::::TreeDoesnotExist)?, - Some(tree) => { - let mut tree_index = 0; - let mut current_draw_number = draw_number % tree.nodes[0]; - - while (tree.k * tree_index) + 1 < (tree.nodes.len() as u64) { - for i in 1..tree.k + 1 { - let node_index = (tree.k * tree_index) + i; - let node_value = tree.nodes[node_index as usize]; - - if current_draw_number >= node_value { - current_draw_number -= node_value; - } else { - tree_index = node_index; - break; - } - } - } - let account_id = tree.node_indexes_to_ids.get(&tree_index).unwrap().clone(); - Ok(account_id) - }, - } - } - - /** - * @dev Query the leaves of a tree. Note that if `startIndex == 0`, the tree is empty and the root node will be returned. - * @param key The key of the tree to get the leaves from. - * @param cursor The pagination cursor. - * @param count The number of items to return. - * @return The index at which leaves start, the values of the returned leaves, and whether there are more for pagination. - * `O(n)` where - * `n` is the maximum number of nodes ever appended. - */ - pub fn query_leafs( - key: SumTreeNameType, - cursor: u64, - count: u64, - ) -> Result<(u64, Vec, bool), DispatchError> { - let tree_option = >::get(&key); - - match tree_option { - None => Err(Error::::TreeDoesnotExist)?, - Some(tree) => { - let mut start_index = 0; - for i in 0..tree.nodes.len() { - if (tree.k * i as u64) + 1 >= tree.nodes.len() as u64 { - start_index = i as u64; - break; - } - } - let loop_start_index = start_index + cursor; - - // let value = if loop_start_index + count > tree.nodes.len() as u64 { - // tree.nodes.len() as u64 - loop_start_index - // } else { - // count - // }; - - let mut values = Vec::new(); - let mut values_index = 0; - let mut has_more = false; - for j in loop_start_index..tree.nodes.len() as u64 { - if values_index < count { - values.push(tree.nodes[j as usize]); - values_index = values_index + 1; - } else { - has_more = true; - break; - } - } - - Ok((start_index, values, has_more)) - }, - } - } - - pub fn remove_tree(key: SumTreeNameType) -> DispatchResult { - >::remove(&key); - Ok(()) - } -} diff --git a/pallets/sortition-sum-game/src/lib.rs b/pallets/sortition-sum-game/src/lib.rs deleted file mode 100644 index 3162357..0000000 --- a/pallets/sortition-sum-game/src/lib.rs +++ /dev/null @@ -1,109 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -mod extras; -pub mod types; - -use crate::types::{SortitionSumTree, SumTreeName}; -use frame_support::sp_std::{collections::btree_map::BTreeMap, vec::Vec}; -use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; -use sortition_sum_game_link::SortitionSumGameLink; -type AccountIdOf = ::AccountId; -pub type BlockNumberOf = ::BlockNumber; -type SumTreeNameType = SumTreeName, BlockNumberOf>; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: frame_system::Config { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { something: u32, who: T::AccountId }, - } - - #[pallet::storage] - #[pallet::getter(fn sortition_sum_trees)] - pub type SortitionSumTrees = - StorageMap<_, Blake2_128Concat, SumTreeNameType, SortitionSumTree>>; - - #[pallet::error] - pub enum Error { - NoneValue, - StorageOverflow, - KMustGreaterThanOne, - TreeAlreadyExists, - TreeDoesnotExist, - } - - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. - #[pallet::call] - impl Pallet { - /// An example dispatchable that takes a singles value as a parameter, writes the value to - /// storage and emits an event. This function must be dispatched by a signed extrinsic. - - /// An example dispatchable that may throw a custom error. - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::cause_error())] - pub fn cause_error(origin: OriginFor) -> DispatchResult { - let _who = ensure_signed(origin)?; - - // Read a value from storage. - match >::get() { - // Return an error if the value has not been set. - None => return Err(Error::::NoneValue.into()), - Some(old) => { - // Increment the value read from storage; will error in the event of overflow. - let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; - // Update the value in storage with the incremented result. - >::put(new); - Ok(()) - }, - } - } - } -} diff --git a/pallets/sortition-sum-game/src/mock.rs b/pallets/sortition-sum-game/src/mock.rs deleted file mode 100644 index b4d6905..0000000 --- a/pallets/sortition-sum-game/src/mock.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate as pallet_template; -use frame_support::traits::{ConstU16, ConstU64}; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - TemplateModule: pallet_template, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() -} diff --git a/pallets/sortition-sum-game/src/tests.rs b/pallets/sortition-sum-game/src/tests.rs deleted file mode 100644 index 8eb60f3..0000000 --- a/pallets/sortition-sum-game/src/tests.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{mock::*, types::SumTreeName, Error, Event}; -use frame_support::{assert_noop, assert_ok}; - -#[test] -fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - let key = SumTreeName::ProfileValidation { citizen_address: 1, block_number: 10 }; - assert_ok!(TemplateModule::create_tree(key.clone(), 5)); - assert_ok!(TemplateModule::set(key.clone(), 10, 1)); - assert_ok!(TemplateModule::set(key.clone(), 20, 1)); - assert_ok!(TemplateModule::set(key.clone(), 30, 2)); - assert_ok!(TemplateModule::set(key.clone(), 40, 3)); - assert_ok!(TemplateModule::set(key.clone(), 50, 4)); - assert_eq!(TemplateModule::stake_of(key.clone(), 1), Ok(Some(20))); - assert_eq!(TemplateModule::draw(key.clone(), 90), Ok(4)); - }); -} - -#[test] -fn correct_error_for_none_value() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let key = SumTreeName::ProfileValidation { citizen_address: 1, block_number: 10 }; - assert_ok!(TemplateModule::create_tree(key.clone(), 2)); - assert_ok!(TemplateModule::set(key.clone(), 10, 1)); - assert_ok!(TemplateModule::set(key.clone(), 20, 1)); - assert_ok!(TemplateModule::set(key.clone(), 30, 2)); - assert_ok!(TemplateModule::set(key.clone(), 40, 3)); - let data2 = TemplateModule::query_leafs(key.clone(), 0, 5); - println!("{:?}", data2); - assert_ok!(TemplateModule::set(key.clone(), 50, 4)); - assert_ok!(TemplateModule::set(key.clone(), 0, 3)); - - let data2 = TemplateModule::query_leafs(key.clone(), 0, 5); - println!("{:?}", data2); - - let data = TemplateModule::draw(key.clone(), 98); - println!("{:?}", data); - }); -} diff --git a/pallets/sortition-sum-game/src/types.rs b/pallets/sortition-sum-game/src/types.rs deleted file mode 100644 index 2c77d08..0000000 --- a/pallets/sortition-sum-game/src/types.rs +++ /dev/null @@ -1,24 +0,0 @@ -use frame_support::pallet_prelude::*; -use frame_support::sp_std::{collections::btree_map::BTreeMap, vec::Vec}; -use scale_info::TypeInfo; - -type CitizenId = u64; - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum SumTreeName { - ProfileValidation { citizen_address: AccountId, block_number: BlockNumber }, - PositiveExternality { user_address: AccountId, block_number: BlockNumber }, - DepartmentRequiredFund { department_required_fund_id: u64, block_number: BlockNumber }, - ProjectTips { project_id: u64, block_number: BlockNumber }, -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct SortitionSumTree { - pub k: u64, - pub stack: Vec, - pub nodes: Vec, - pub ids_to_node_indexes: BTreeMap, // citizen id, node index - pub node_indexes_to_ids: BTreeMap, // node index, citizen id -} diff --git a/pallets/spaces/Cargo.toml b/pallets/spaces/Cargo.toml deleted file mode 100644 index 2404494..0000000 --- a/pallets/spaces/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "pallet-spaces" -version = "4.0.0-dev" -description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive", -] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-timestamp = { git = 'https://github.com/paritytech/substrate', branch = "polkadot-v0.9.42", default-features = false } - -pallet-support = { default-features = false, path = '../support' } - - -[dev-dependencies] -sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", -] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/spaces/src/benchmarking.rs b/pallets/spaces/src/benchmarking.rs deleted file mode 100644 index 5a26241..0000000 --- a/pallets/spaces/src/benchmarking.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; - -#[benchmarks] -mod benchmarks { - use super::*; - - #[benchmark] - fn do_something() { - let value = 100u32.into(); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - do_something(RawOrigin::Signed(caller), value); - - assert_eq!(Something::::get(), Some(value)); - } - - #[benchmark] - fn cause_error() { - Something::::put(100u32); - let caller: T::AccountId = whitelisted_caller(); - #[extrinsic_call] - cause_error(RawOrigin::Signed(caller)); - - assert_eq!(Something::::get(), Some(101u32)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/spaces/src/extras.rs b/pallets/spaces/src/extras.rs deleted file mode 100644 index cb85d65..0000000 --- a/pallets/spaces/src/extras.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::*; - -impl Pallet { - /// Get `Space` by id from the storage or return `SpaceNotFound` error. - pub fn require_space(space_id: SpaceId) -> Result, DispatchError> { - Ok(Self::space_by_id(space_id).ok_or(Error::::SpaceNotFound)?) - } -} diff --git a/pallets/spaces/src/lib.rs b/pallets/spaces/src/lib.rs deleted file mode 100644 index 722a7fd..0000000 --- a/pallets/spaces/src/lib.rs +++ /dev/null @@ -1,138 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// Edit this file to define custom logic or remove it if it is not needed. -/// Learn more about FRAME and the core library of Substrate FRAME pallets: -/// -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod weights; -pub use weights::*; - -pub mod extras; -pub mod types; - -use frame_support::sp_std::prelude::*; -// use scale_info::prelude::format; -use crate::types::RESERVED_SPACE_COUNT; - -use frame_support::pallet_prelude::{DispatchResult, *}; -use frame_system::pallet_prelude::*; -use pallet_support::{Content, SpaceId}; -use types::Space; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: frame_system::Config + pallet_timestamp::Config { - /// Because this pallet emits events, it depends on the runtime's definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - } - - // The pallet's runtime storage items. - // https://docs.substrate.io/main-docs/build/runtime-storage/ - #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/main-docs/build/runtime-storage/#declaring-storage-items - pub type Something = StorageValue<_, u32>; - - #[pallet::type_value] - pub fn DefaultForNextSpaceId() -> SpaceId { - RESERVED_SPACE_COUNT + 1 - } - - /// The next space id. - #[pallet::storage] - #[pallet::getter(fn next_space_id)] - pub type NextSpaceId = StorageValue<_, SpaceId, ValueQuery, DefaultForNextSpaceId>; - - /// Get the details of a space by its' id. - #[pallet::storage] - #[pallet::getter(fn space_by_id)] - pub type SpaceById = StorageMap<_, Twox64Concat, SpaceId, Space>; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/main-docs/build/events-errors/ - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored { something: u32, who: T::AccountId }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, - - SpaceNotFound, - } - - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. - #[pallet::call] - impl Pallet { - /// An example dispatchable that takes a singles value as a parameter, writes the value to - /// storage and emits an event. This function must be dispatched by a signed extrinsic. - #[pallet::call_index(0)] - #[pallet::weight(::WeightInfo::do_something())] - pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - // This function will return an error if the extrinsic is not signed. - // https://docs.substrate.io/main-docs/build/origins/ - let who = ensure_signed(origin)?; - - // Update storage. - >::put(something); - - // Emit an event. - Self::deposit_event(Event::SomethingStored { something, who }); - // Return a successful DispatchResultWithPostInfo - Ok(()) - } - - /// An example dispatchable that may throw a custom error. - #[pallet::call_index(1)] - #[pallet::weight(::WeightInfo::cause_error())] - pub fn cause_error(origin: OriginFor) -> DispatchResult { - let _who = ensure_signed(origin)?; - - // Read a value from storage. - match >::get() { - // Return an error if the value has not been set. - None => return Err(Error::::NoneValue.into()), - Some(old) => { - // Increment the value read from storage; will error in the event of overflow. - let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; - // Update the value in storage with the incremented result. - >::put(new); - Ok(()) - }, - } - } - } -} diff --git a/pallets/spaces/src/mock.rs b/pallets/spaces/src/mock.rs deleted file mode 100644 index 48186f4..0000000 --- a/pallets/spaces/src/mock.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate as pallet_template; -use frame_support::{ - parameter_types, - traits::{ConstU16, ConstU64}, -}; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - TemplateModule: pallet_template, - Timestamp: pallet_timestamp, - } -); - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_template::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -parameter_types! { - pub const MinimumPeriod: u64 = 5; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() -} diff --git a/pallets/spaces/src/tests.rs b/pallets/spaces/src/tests.rs deleted file mode 100644 index 7c2b853..0000000 --- a/pallets/spaces/src/tests.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{mock::*, Error, Event}; -use frame_support::{assert_noop, assert_ok}; - -#[test] -fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - // Dispatch a signed extrinsic. - assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); - // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); - // Assert that the correct event was deposited - System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into()); - }); -} - -#[test] -fn correct_error_for_none_value() { - new_test_ext().execute_with(|| { - // Ensure the expected error is thrown when no value is present. - assert_noop!( - TemplateModule::cause_error(RuntimeOrigin::signed(1)), - Error::::NoneValue - ); - }); -} diff --git a/pallets/spaces/src/types.rs b/pallets/spaces/src/types.rs deleted file mode 100644 index 0a2c2ef..0000000 --- a/pallets/spaces/src/types.rs +++ /dev/null @@ -1,55 +0,0 @@ -use frame_support::pallet_prelude::*; -use frame_support::sp_std::vec::Vec; -use scale_info::TypeInfo; - -use super::*; - -use pallet_support::{new_who_and_when, WhoAndWhenOf}; - -pub const FIRST_SPACE_ID: u64 = 1; -pub const RESERVED_SPACE_COUNT: u64 = 1000; - -/// Information about a space's owner, its' content, visibility and custom permissions. -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct Space { - /// Unique sequential identifier of a space. Examples of space ids: `1`, `2`, `3`, and so on. - pub id: SpaceId, - - pub created: WhoAndWhenOf, - /// True, if the content of this space was edited. - pub edited: bool, - - /// The current owner of a given space. - pub owner: T::AccountId, - - // The next fields can be updated by the owner: - pub content: Content, - - /// Hidden field is used to recommend to end clients (web and mobile apps) that a particular - /// space and its' posts should not be shown. - pub hidden: bool, -} - -#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, RuntimeDebug, TypeInfo)] -pub struct SpaceUpdate { - pub content: Option, - pub hidden: Option, -} - -impl Space { - pub fn new(id: SpaceId, created_by: T::AccountId, content: Content) -> Self { - Space { - id, - created: new_who_and_when::(created_by.clone()), - edited: false, - owner: created_by, - content, - hidden: false, - } - } - - pub fn is_owner(&self, account: &T::AccountId) -> bool { - self.owner == *account - } -} diff --git a/pallets/stream-payment/Cargo.toml b/pallets/stream-payment/Cargo.toml new file mode 100644 index 0000000..6347a7e --- /dev/null +++ b/pallets/stream-payment/Cargo.toml @@ -0,0 +1,76 @@ +[package] +name = "pallet-stream-payment" +authors = { workspace = true } +description = "Stream payment pallet" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +log = { workspace = true } +serde = { workspace = true, optional = true } + +dp-core = { workspace = true } +tp-maths = { workspace = true } +tp-traits = { workspace = true } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +num-traits = { workspace = true } +pallet-balances = { workspace = true, features = [ "std" ] } +similar-asserts = { workspace = true } +sp-io = { workspace = true, features = [ "std" ] } +tap = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "dp-core/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances/std", + "parity-scale-codec/std", + "scale-info/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "tp-maths/std", + "tp-traits/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "tp-maths/runtime-benchmarks", + "tp-traits/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/stream-payment/README.md b/pallets/stream-payment/README.md new file mode 100644 index 0000000..c85fd90 --- /dev/null +++ b/pallets/stream-payment/README.md @@ -0,0 +1,57 @@ +# Stream payment pallet + +A pallet to create payment streams, where users can setup recurrent payment at some rate per unit of +time. The pallet aims to be configurable and usage agnostic: + +- Runtime configures which assets are supported by providing an `AssetId` type and a type + implementing the `Assets` trait which only requires function needed by the pallet (increase + deposit when creating or refilling a stream, decrease deposit when closing a stream, and + transferring a deposit when the stream payment is performed). Both types allows to easily add new + supported assets in the future while being retro-compatible. The pallet make few assumptions about + how the funds are deposited (thanks to the custom trait), which should allow to easily support + assets from various pallets/sources. +- Runtime configure which unit of time is supported to express the rate of payment. Units of time + should be monotonically increasing. Users can then choose which unit of time they want to use. + +The pallet provides the following calls: +- `open_stream(target, time_unit, asset_id, rate, initial_deposit)`: The origin creates a stream + towards a target (payee), with given time unit, asset and rate. A deposit is made, which is able + to pay for `initial_deposit / rate`. Streams are indexed using a `StreamId` which is returned with + an event. +- `perform_payment(stream_id)`: can be called by anyone to update a stream, performing the payment + for the elapsed time since the last update. All other calls implicitly call `perform_payment`, + such that at any point in time you're guaranteed you'll be able to redeem the payment for the + elapsed time; which allow to call it only when the funds are needed without fear of non-payment. +- `close_stream(stream_id)`: only callable by the source or target of the stream. It pays for the + elapsed time then refund the remaining deposit to the source. +- `immediately_change_deposit(stream_id, asset_id, change)`: Change the deposit in the stream. It + first perform a payment before applying the change, which means a source will not retro-actively + pay for a drained stream. A target that provides services in exchange for payment should suspend + the service as soon as updating the stream would make it drain, and should resume services once + the stream is refilled. The call takes an asset id which must match the config asset id, which + prevents unwanted amounts when a change request that changes the asset is accepted. +- `request_change(stream_id, kind, new_config, deposit_change)`: Allows to request changing the + config of the stream. `kind` states if the change is a mere suggestion or is mandatory, in which + case there is a provided deadline at which point payments will no longer occur. Requests that + don't change the time unit or asset id and change the rate at a disadvantage for the caller is + applied immediately. An existing request can be overritten by both parties if it was a suggestion, + while only by the previous requester if it was mandatory. A nonce is increased to prevent to + prevent one to frontrunner the acceptation of a request with another request. The target of the + stream cannot provide a deposit change, while the source can. It is however mandatory to provide + change with absolute value when changing asset. +- `accept_requested_change(stream_id, request_nonce, deposit_change)`: Accept the change for this + stream id and request nonce. If one want to refuse a change they can either leave it as is (which + will do nothing if the request is a suggestion, or stop payment when reaching the deadline if + mandatory) or close the stream with `close_stream`. The target of the stream cannot provide a + deposit change, while the source can. It is however mandatory to provide change with absolute + value when changing asset. +- `cancel_change_request(stream_id)`: Cancel a change request, only callable by the requester of a + previous request. + +For UIs the pallet provides the following storages: +- `Streams: StreamId => Stream`: stream data indexed by stream id. +- `LookupStreamsWithSource: AccountId => StreamId => ()`: allows to list allow the streams with a + given source by iterating over all storage keys with the key prefix corresponding to the account. +- `LookupStreamsWithTarget: AccountId => StreamId => ()`: same but for the target. Those last 2 + storages are solely for UIs to list incoming and outgoing streams. Key prefix is used to reduce + the POV cost that would require a single Vec of StreamId. \ No newline at end of file diff --git a/pallets/stream-payment/rpc/runtime-api/Cargo.toml b/pallets/stream-payment/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000..0f189c5 --- /dev/null +++ b/pallets/stream-payment/rpc/runtime-api/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "pallet-stream-payment-runtime-api" +authors = { workspace = true } +description = "Runtime API definition of pallet-stream-payment" +edition = "2021" +license = "GPL-3.0-only" +version = "0.1.0" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[lints] +workspace = true + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +serde = { workspace = true, optional = true, features = [ "derive" ] } +sp-api = { workspace = true } +thiserror = { workspace = true, optional = true } + +[features] +default = [ "std" ] +std = [ + "parity-scale-codec/std", + "scale-info/std", + "serde", + "serde?/std", + "sp-api/std", + "thiserror", +] diff --git a/pallets/stream-payment/rpc/runtime-api/src/lib.rs b/pallets/stream-payment/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000..c65b696 --- /dev/null +++ b/pallets/stream-payment/rpc/runtime-api/src/lib.rs @@ -0,0 +1,65 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Runtime API for Stream Payment pallet + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use { + alloc::string::String, + parity_scale_codec::{Decode, Encode}, +}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct StreamPaymentApiStatus { + pub payment: Balance, + pub deposit_left: Balance, + /// Whenever the stream is stalled, which can occur either when no funds are left or + /// if the time is past a mandatory request deadline. + pub stalled: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(thiserror::Error))] +pub enum StreamPaymentApiError { + #[cfg_attr(feature = "std", error("Unknown stream id"))] + UnknownStreamId, + #[cfg_attr(feature = "std", error("Other error: {0}"))] + Other(String), +} + +sp_api::decl_runtime_apis! { + pub trait StreamPaymentApi + where + StreamId: parity_scale_codec::Codec, + Instant: parity_scale_codec::Codec, + Balance: parity_scale_codec::Codec, + { + /// Get the stream payment current status, telling how much payment is + /// pending, how much deposit will be left and whenever the stream is stalled. + /// The stream is considered stalled if no funds are left or if the provided + /// time is past a mandatory request deadline. If the provided `now` is `None` + /// then the current time will be fetched. Being able to provide a custom `now` + /// allows to check the status in the future. + fn stream_payment_status( + stream_id: StreamId, + now: Option, + ) -> Result, StreamPaymentApiError>; + } +} diff --git a/pallets/stream-payment/src/benchmarking.rs b/pallets/stream-payment/src/benchmarking.rs new file mode 100644 index 0000000..474fb73 --- /dev/null +++ b/pallets/stream-payment/src/benchmarking.rs @@ -0,0 +1,436 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +use { + crate::{ + Assets, Call, ChangeKind, Config, DepositChange, Event, Pallet, Party, StreamConfig, + Streams, TimeProvider, + }, + frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}, + frame_support::{assert_ok, dispatch::RawOrigin}, + frame_system::EventRecord, +}; + +/// Create a funded user. +fn create_funded_user( + string: &'static str, + n: u32, + asset_id: &T::AssetId, + // amount: T::Balance, +) -> T::AccountId { + const SEED: u32 = 0; + let user = account(string, n, SEED); + + // create a large amount that should be greater than ED + let amount: T::Balance = 1_000_000_000u32.into(); + let amount: T::Balance = amount * T::Balance::from(1_000_000_000u32); + T::Assets::bench_set_balance(asset_id, &user, amount); + user +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn open_stream() -> Result<(), BenchmarkError> { + let asset_id = T::Assets::bench_worst_case_asset_id(); + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + #[extrinsic_call] + _( + RawOrigin::Signed(source.clone()), + target, + StreamConfig { + time_unit, + asset_id, + rate: 100u32.into(), + }, + 1_000_000u32.into(), + ); + + assert_last_event::( + Event::StreamOpened { + stream_id: 0u32.into(), + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn close_stream() -> Result<(), BenchmarkError> { + // Worst case is closing a stream with a pending payment. + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + let asset_id = T::Assets::bench_worst_case_asset_id(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + let rate = 100u32.into(); + let initial_deposit = 1_000_000u32.into(); + + assert_ok!(Pallet::::open_stream( + RawOrigin::Signed(source.clone()).into(), + target, + StreamConfig { + time_unit: time_unit.clone(), + asset_id, + rate, + }, + initial_deposit, + )); + + // Change time to trigger payment. + let now = T::TimeProvider::now(&time_unit).expect("can fetch time"); + let delta: T::Balance = 10u32.into(); + T::TimeProvider::bench_set_now(now + delta); + + #[extrinsic_call] + _(RawOrigin::Signed(source.clone()), 0u32.into()); + + assert_last_event::( + Event::StreamClosed { + stream_id: 0u32.into(), + refunded: initial_deposit - (rate * delta), + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn perform_payment() -> Result<(), BenchmarkError> { + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + let asset_id = T::Assets::bench_worst_case_asset_id(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + let rate = 100u32.into(); + let initial_deposit = 1_000_000u32.into(); + + assert_ok!(Pallet::::open_stream( + RawOrigin::Signed(source.clone()).into(), + target.clone(), + StreamConfig { + time_unit: time_unit.clone(), + asset_id, + rate, + }, + initial_deposit, + )); + + // Change time to trigger payment. + let now = T::TimeProvider::now(&time_unit).expect("can fetch time"); + let delta: T::Balance = 10u32.into(); + T::TimeProvider::bench_set_now(now + delta); + + #[extrinsic_call] + _(RawOrigin::Signed(source.clone()), 0u32.into()); + + assert_last_event::( + Event::StreamPayment { + stream_id: 0u32.into(), + source, + target, + amount: rate * delta, + stalled: false, + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn request_change_immediate() -> Result<(), BenchmarkError> { + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + let asset_id = T::Assets::bench_worst_case_asset_id(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + let rate = 100u32.into(); + let initial_deposit = 1_000_000u32.into(); + let config = StreamConfig { + time_unit: time_unit.clone(), + asset_id, + rate, + }; + + assert_ok!(Pallet::::open_stream( + RawOrigin::Signed(source.clone()).into(), + target, + config.clone(), + initial_deposit, + )); + + let new_config = StreamConfig { + rate: 101u32.into(), + ..config.clone() + }; + + #[extrinsic_call] + Pallet::::request_change( + RawOrigin::Signed(source.clone()), + 0u32.into(), + ChangeKind::Suggestion, + new_config.clone(), + Some(DepositChange::Increase(1_000u32.into())), + ); + + assert_last_event::( + Event::StreamConfigChanged { + stream_id: 0u32.into(), + old_config: config, + new_config, + deposit_change: Some(DepositChange::Increase(1_000u32.into())), + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn request_change_delayed() -> Result<(), BenchmarkError> { + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + let asset_id = T::Assets::bench_worst_case_asset_id(); + let asset_id2 = T::Assets::bench_worst_case_asset_id2(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + let rate = 100u32.into(); + let initial_deposit = 1_000_000u32.into(); + let config = StreamConfig { + time_unit: time_unit.clone(), + asset_id, + rate, + }; + + assert_ok!(Pallet::::open_stream( + RawOrigin::Signed(source.clone()).into(), + target, + config.clone(), + initial_deposit, + )); + + // Change the asset id. In the case asset_id == asset_id2, we decrease the rate so that + // the request is not executed immediately. + let new_config = StreamConfig { + asset_id: asset_id2, + rate: 99u32.into(), + ..config.clone() + }; + + let stream_id = 0u32.into(); + + #[extrinsic_call] + Pallet::::request_change( + RawOrigin::Signed(source.clone()), + stream_id, + ChangeKind::Suggestion, + new_config.clone(), + Some(DepositChange::Absolute(500u32.into())), + ); + + assert_last_event::( + Event::StreamConfigChangeRequested { + stream_id, + request_nonce: 1, + requester: Party::Source, + old_config: config, + new_config, + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn accept_requested_change() -> Result<(), BenchmarkError> { + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + let asset_id = T::Assets::bench_worst_case_asset_id(); + let asset_id2 = T::Assets::bench_worst_case_asset_id2(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + let rate = 100u32.into(); + let initial_deposit = 1_000_000u32.into(); + let config = StreamConfig { + time_unit: time_unit.clone(), + asset_id, + rate, + }; + + assert_ok!(Pallet::::open_stream( + RawOrigin::Signed(source.clone()).into(), + target.clone(), + config.clone(), + initial_deposit, + )); + + // Change the asset id. In the case asset_id == asset_id2, we decrease the rate so that + // the request is not executed immediately. + let new_config = StreamConfig { + asset_id: asset_id2, + rate: 99u32.into(), + ..config.clone() + }; + + assert_ok!(Pallet::::request_change( + RawOrigin::Signed(source.clone()).into(), + 0u32.into(), + ChangeKind::Suggestion, + new_config.clone(), + Some(DepositChange::Absolute(500u32.into())), + )); + + #[extrinsic_call] + _(RawOrigin::Signed(target.clone()), 0u32.into(), 1, None); + + assert_last_event::( + Event::StreamConfigChanged { + stream_id: 0u32.into(), + old_config: config, + new_config, + deposit_change: Some(DepositChange::Absolute(500u32.into())), + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn cancel_change_request() -> Result<(), BenchmarkError> { + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + let asset_id = T::Assets::bench_worst_case_asset_id(); + let asset_id2 = T::Assets::bench_worst_case_asset_id2(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + let rate = 100u32.into(); + let initial_deposit = 1_000_000u32.into(); + let config = StreamConfig { + time_unit: time_unit.clone(), + asset_id, + rate, + }; + + assert_ok!(Pallet::::open_stream( + RawOrigin::Signed(source.clone()).into(), + target.clone(), + config.clone(), + initial_deposit, + )); + + // Change the asset id. In the case asset_id == asset_id2, we decrease the rate so that + // the request is not executed immediately. + let new_config = StreamConfig { + asset_id: asset_id2, + rate: 99u32.into(), + ..config.clone() + }; + + assert_ok!(Pallet::::request_change( + RawOrigin::Signed(source.clone()).into(), + 0u32.into(), + ChangeKind::Suggestion, + new_config.clone(), + Some(DepositChange::Absolute(500u32.into())), + )); + + #[extrinsic_call] + _(RawOrigin::Signed(source), 0u32.into()); + + let stream_id: T::StreamId = 0u32.into(); + assert!(Streams::::get(stream_id) + .expect("to be a stream") + .pending_request + .is_none()); + + Ok(()) + } + + #[benchmark] + fn immediately_change_deposit() -> Result<(), BenchmarkError> { + let time_unit = T::TimeProvider::bench_worst_case_time_unit(); + let asset_id = T::Assets::bench_worst_case_asset_id(); + + let source = create_funded_user::("source", 1, &asset_id); + let target = create_funded_user::("target", 2, &asset_id); + + let rate = 100u32.into(); + let initial_deposit = 1_000_000u32.into(); + let config = StreamConfig { + time_unit: time_unit.clone(), + asset_id: asset_id.clone(), + rate, + }; + + assert_ok!(Pallet::::open_stream( + RawOrigin::Signed(source.clone()).into(), + target.clone(), + config.clone(), + initial_deposit, + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(source), + 0u32.into(), + asset_id, + DepositChange::Absolute(500u32.into()), + ); + + assert_last_event::( + Event::StreamConfigChanged { + stream_id: 0u32.into(), + old_config: config.clone(), + new_config: config, + deposit_change: Some(DepositChange::Absolute(500u32.into())), + } + .into(), + ); + + Ok(()) + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExtBuilder::default().build(), + crate::mock::Runtime, + ); +} diff --git a/pallets/stream-payment/src/lib.rs b/pallets/stream-payment/src/lib.rs new file mode 100644 index 0000000..15ac50d --- /dev/null +++ b/pallets/stream-payment/src/lib.rs @@ -0,0 +1,1036 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#![doc = include_str!("../README.md")] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub mod weights; +pub use weights::WeightInfo; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +use { + core::cmp::min, + frame_support::{ + dispatch::DispatchErrorWithPostInfo, + pallet, + pallet_prelude::*, + storage::types::{StorageDoubleMap, StorageMap}, + traits::{ + fungible::{Inspect, MutateHold}, + tokens::{Balance, Precision}, + }, + Blake2_128Concat, + }, + frame_system::pallet_prelude::*, + parity_scale_codec::{FullCodec, MaxEncodedLen}, + scale_info::TypeInfo, + sp_runtime::{ + traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, One, Saturating, Zero}, + ArithmeticError, + }, + sp_std::{fmt::Debug, marker::PhantomData}, +}; + +pub use pallet::*; + +/// Type able to provide the current time for given unit. +/// For each unit the returned number should monotonically increase and not +/// overflow. +pub trait TimeProvider { + fn now(unit: &Unit) -> Option; + + /// Benchmarks: should return the time unit which has the worst performance calling + /// `TimeProvider::now(unit)` with. + #[cfg(feature = "runtime-benchmarks")] + fn bench_worst_case_time_unit() -> Unit; + + /// Benchmarks: sets the "now" time for time unit returned by `bench_worst_case_time_unit`. + #[cfg(feature = "runtime-benchmarks")] + fn bench_set_now(instant: Number); +} + +/// Interactions the pallet needs with assets. +pub trait Assets { + /// Transfer assets deposited by an account to another account. + /// Those assets should not be considered deposited in the target account. + fn transfer_deposit( + asset_id: &AssetId, + from: &AccountId, + to: &AccountId, + amount: Balance, + ) -> DispatchResult; + + /// Increase the deposit for an account and asset id. Should fail if account doesn't have + /// enough of that asset. Funds should be safe and not slashable. + fn increase_deposit(asset_id: &AssetId, account: &AccountId, amount: Balance) + -> DispatchResult; + + /// Decrease the deposit for an account and asset id. Should fail on underflow. + fn decrease_deposit(asset_id: &AssetId, account: &AccountId, amount: Balance) + -> DispatchResult; + + /// Return the deposit for given asset and account. + fn get_deposit(asset_id: &AssetId, account: &AccountId) -> Balance; + + /// Benchmarks: should return the asset id which has the worst performance when interacting + /// with it. + #[cfg(feature = "runtime-benchmarks")] + fn bench_worst_case_asset_id() -> AssetId; + + /// Benchmarks: should return the another asset id which has the worst performance when interacting + /// with it afther `bench_worst_case_asset_id`. This is to benchmark the worst case when changing config + /// from one asset to another. If there is only one asset id it is fine to return it in both + /// `bench_worst_case_asset_id` and `bench_worst_case_asset_id2`. + #[cfg(feature = "runtime-benchmarks")] + fn bench_worst_case_asset_id2() -> AssetId; + + /// Benchmarks: should set the balance. + #[cfg(feature = "runtime-benchmarks")] + fn bench_set_balance(asset_id: &AssetId, account: &AccountId, amount: Balance); +} + +#[pallet] +pub mod pallet { + use super::*; + + /// Pooled Staking pallet. + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Overarching event type + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Type used to represent stream ids. Should be large enough to not overflow. + type StreamId: AtLeast32BitUnsigned + + Default + + Debug + + Copy + + Clone + + FullCodec + + TypeInfo + + MaxEncodedLen; + + /// The balance type, which is also the type representing time (as this + /// pallet will do math with both time and balances to compute how + /// much should be paid). + type Balance: Balance; + + /// Type representing an asset id, a identifier allowing distinguishing assets. + type AssetId: Debug + Clone + FullCodec + TypeInfo + MaxEncodedLen + PartialEq + Eq; + + /// Provide interaction with assets. + type Assets: Assets; + + /// Currency for the opening balance hold for the storage used by the Stream. + /// NOT to be confused with Assets. + type Currency: Inspect + + MutateHold; + + type RuntimeHoldReason: From; + + #[pallet::constant] + type OpenStreamHoldAmount: Get; + + /// Represents which units of time can be used. Designed to be an enum + /// with a variant for each kind of time source/scale supported. + type TimeUnit: Debug + Clone + FullCodec + TypeInfo + MaxEncodedLen + Eq; + + /// Provide the current time in given unit. + type TimeProvider: TimeProvider; + + type WeightInfo: weights::WeightInfo; + } + + type AccountIdOf = ::AccountId; + type AssetIdOf = ::AssetId; + + pub type RequestNonce = u32; + + /// A stream payment from source to target. + /// Stores the last time the stream was updated, which allows to compute + /// elapsed time and perform payment. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Clone, TypeInfo)] + pub struct Stream { + /// Payer, source of the stream. + pub source: AccountId, + /// Payee, target of the stream. + pub target: AccountId, + /// Steam config (time unit, asset id, rate) + pub config: StreamConfig, + /// How much is deposited to fund this stream. + pub deposit: Balance, + /// Last time the stream was updated in `config.time_unit`. + pub last_time_updated: Balance, + /// Nonce for requests. This prevents a request to make a first request + /// then change it to another request to frontrun the other party + /// accepting. + pub request_nonce: RequestNonce, + /// A pending change request if any. + pub pending_request: Option>, + /// One-time opening deposit. Will be released on close. + pub opening_deposit: Balance, + } + + impl Stream { + pub fn account_to_party(&self, account: AccountId) -> Option { + match account { + a if a == self.source => Some(Party::Source), + a if a == self.target => Some(Party::Target), + _ => None, + } + } + } + + /// Stream configuration. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Copy, Clone, TypeInfo)] + pub struct StreamConfig { + /// Unit in which time is measured using a `TimeProvider`. + pub time_unit: Unit, + /// Asset used for payment. + pub asset_id: AssetId, + /// Amount of asset / unit. + pub rate: Balance, + } + + /// Origin of a change request. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Copy, Clone, TypeInfo)] + pub enum Party { + Source, + Target, + } + + impl Party { + pub fn inverse(self) -> Self { + match self { + Party::Source => Party::Target, + Party::Target => Party::Source, + } + } + } + + /// Kind of change requested. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Copy, Clone, TypeInfo)] + pub enum ChangeKind